Skip to content

Commit 0c33118

Browse files
committed
tests: cover missing pddikti/nim branches
1 parent 2988c80 commit 0c33118

6 files changed

Lines changed: 168 additions & 31 deletions

File tree

.github/workflows/ci.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,18 @@ on:
1111
jobs:
1212
ci:
1313
runs-on: ubuntu-latest
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
php: ["7.4", "8.0", "8.1", "8.2", "8.3"]
1418
steps:
1519
- uses: actions/checkout@v3
1620
- uses: shivammathur/setup-php@v2
1721
with:
18-
php-version: "7.4"
22+
php-version: ${{ matrix.php }}
1923
coverage: xdebug
2024
- run: composer self-update && composer install && composer dump-autoload
25+
- run: composer psr2check
2126
- run: vendor/bin/phpunit --coverage-clover coverage.xml tests
2227
- uses: codecov/codecov-action@v3
2328
env:

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Sekolah Tinggi Teknologi Wastukancana Student ID (NIM) Parser.
1212

1313
## Requirements
1414

15-
- PHP `>= 7.4`
15+
- PHP `>= 7.4` (tested on 7.4, 8.0, 8.1, 8.2, 8.3)
1616
- Composer v2
1717
- cURL `>= 7.19.4`
1818

@@ -30,18 +30,29 @@ composer require wastukancana/nim
3030
<?php
3131

3232
use Wastukancana\Nim;
33+
use Exception;
3334

3435
require __DIR__ . '/vendor/autoload.php';
3536

3637
try {
3738
$nim = new Nim('211351143');
38-
3939
var_dump($nim->dump());
40-
} catch (\Exception $e) {
40+
} catch (Exception $e) {
4141
echo $e->getMessage();
4242
}
4343
```
4444

45+
## Development
46+
47+
Run code style checks and tests locally:
48+
49+
```bash
50+
composer psr2check
51+
php vendor/bin/phpunit --testdox tests
52+
```
53+
54+
This repository uses GitHub Actions to run the matrix CI against PHP 7.4 and 8.x with coverage reporting to Codecov.
55+
4556
## License
4657

4758
This project is licensed under [MIT License](./LICENSE).

src/Nim.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Wastukancana;
44

5+
use InvalidArgumentException;
6+
57
class Nim extends Parser
68
{
79
private const MIN_YEAR = 2001;
@@ -19,19 +21,19 @@ public function __construct($nim)
1921
private function isValid(): bool
2022
{
2123
if (strlen($this->nim) < 8 || strlen($this->nim) > 9) {
22-
throw new \InvalidArgumentException('NIM must be 8 or 9 characters');
24+
throw new InvalidArgumentException('NIM must be 8 or 9 characters');
2325
}
2426

2527
if (! ctype_digit($this->nim)) {
26-
throw new \InvalidArgumentException('NIM must contain only numbers');
28+
throw new InvalidArgumentException('NIM must contain only numbers');
2729
}
2830

2931
if (! $this->isValidAdmissionYear()) {
30-
throw new \InvalidArgumentException('Admission year is invalid');
32+
throw new InvalidArgumentException('Admission year is invalid');
3133
}
3234

3335
if (! $this->isValidStudy()) {
34-
throw new \InvalidArgumentException('Study cannot be found');
36+
throw new InvalidArgumentException('Study cannot be found');
3537
}
3638

3739
return true;

src/PDDikti.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Wastukancana;
44

5+
use Exception;
56
use GuzzleHttp\Client;
67
use Psr\Http\Message\ResponseInterface;
78

@@ -41,7 +42,7 @@ private function fetchData(string $endpoint): ?object
4142
'Access-Control-Request-Headers' => 'X-User-Ip',
4243
],
4344
]);
44-
} catch (\Exception $e) {
45+
} catch (Exception $e) {
4546
return null;
4647
}
4748

@@ -54,7 +55,7 @@ private function fetchData(string $endpoint): ?object
5455
]);
5556

5657
return $this->parseResponse($response);
57-
} catch (\Exception $e) {
58+
} catch (Exception $e) {
5859
return null;
5960
}
6061
}

tests/NimParserTest.php

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,27 @@
11
<?php
22

3+
use PHPUnit\Framework\TestCase;
34
use Wastukancana\Nim;
45
use Wastukancana\Student;
5-
use PHPUnit\Framework\TestCase;
66

77
final class NimParserTest extends TestCase
88
{
99
const NIM_TEST = '211351143';
10+
1011
const EXPECTED_NAME = 'SULUH SULISTIAWAN';
12+
1113
const EXPECTED_GENDER = 'M';
14+
1215
const EXPECTED_GRADUATION = false;
16+
1317
const EXPECTED_YEAR = 2021;
18+
1419
const EXPECTED_STUDY = 'Teknik Informatika';
20+
1521
const EXPECTED_LEVEL = 'S1';
22+
1623
const EXPECTED_SEMESTER = 1;
24+
1725
const EXPECTED_SEQUENCE = 143;
1826

1927
private Nim $nim;
@@ -23,17 +31,17 @@ protected function setUp(): void
2331
$this->nim = new Nim(self::NIM_TEST);
2432
}
2533

26-
public function testIsValidAdmissionYear()
34+
public function test_is_valid_admission_year()
2735
{
2836
$this->assertTrue($this->nim->isValidAdmissionYear());
2937
}
3038

31-
public function testIsValidStudy()
39+
public function test_is_valid_study()
3240
{
3341
$this->assertTrue($this->nim->isValidStudy());
3442
}
3543

36-
public function testCanDump()
44+
public function test_can_dump()
3745
{
3846
$dump = $this->nim->dump();
3947

@@ -51,72 +59,78 @@ public function testCanDump()
5159
$this->assertEquals($student, $dump);
5260
}
5361

54-
public function testCanGetNim()
62+
public function test_can_get_nim()
5563
{
5664
$this->assertEquals(self::NIM_TEST, $this->nim->getNIM());
5765
}
5866

59-
public function testCanGetName()
67+
public function test_can_get_name()
6068
{
6169
$this->assertEquals(self::EXPECTED_NAME, $this->nim->getName());
6270
}
6371

64-
public function testCanGetGender()
72+
public function test_can_get_gender()
6573
{
6674
$this->assertEquals(self::EXPECTED_GENDER, $this->nim->getGender());
6775
}
6876

69-
public function testCanGetIsGraduated()
77+
public function test_can_get_is_graduated()
7078
{
7179
$this->assertEquals(self::EXPECTED_GRADUATION, $this->nim->getIsGraduated());
7280
}
7381

74-
public function testCanGetFirstSemester()
82+
public function test_can_get_first_semester()
7583
{
7684
$this->assertEquals(self::EXPECTED_SEMESTER, $this->nim->getFirstSemester());
7785
}
7886

79-
public function testCanGetSequenceNumber()
87+
public function test_can_get_sequence_number()
8088
{
8189
$this->assertEquals(self::EXPECTED_SEQUENCE, $this->nim->getSequenceNumber());
8290
}
8391

84-
public function testCanGetAdmissionYear()
92+
public function test_can_get_admission_year()
8593
{
8694
$this->assertEquals(self::EXPECTED_YEAR, $this->nim->getAdmissionYear());
8795
}
8896

89-
public function testCanGetStudy()
97+
public function test_can_get_study()
9098
{
9199
$this->assertEquals(self::EXPECTED_STUDY, $this->nim->getStudy());
92100
}
93101

94-
public function testCanGetEducationLevel()
102+
public function test_can_get_education_level()
95103
{
96104
$this->assertEquals(self::EXPECTED_LEVEL, $this->nim->getEducationLevel());
97105
}
98106

99-
public function testNimWithInvalidLengthThrowsException()
107+
public function test_nim_with_too_short_length_throws_exception()
100108
{
101-
$this->expectException(\InvalidArgumentException::class);
109+
$this->expectException(InvalidArgumentException::class);
102110
new Nim('1');
103111
}
104112

105-
public function testNimWithNonNumericCharactersThrowsException()
113+
public function test_nim_with_too_long_length_throws_exception()
114+
{
115+
$this->expectException(InvalidArgumentException::class);
116+
new Nim('21135114300');
117+
}
118+
119+
public function test_nim_with_non_numeric_characters_throws_exception()
106120
{
107-
$this->expectException(\InvalidArgumentException::class);
121+
$this->expectException(InvalidArgumentException::class);
108122
new Nim('2113511a3');
109123
}
110124

111-
public function testInvalidAdmissionYearThrowsException()
125+
public function test_invalid_admission_year_throws_exception()
112126
{
113-
$this->expectException(\InvalidArgumentException::class);
127+
$this->expectException(InvalidArgumentException::class);
114128
new Nim('991351143');
115129
}
116130

117-
public function testNonExistentStudyThrowsException()
131+
public function test_non_existent_study_throws_exception()
118132
{
119-
$this->expectException(\InvalidArgumentException::class);
133+
$this->expectException(InvalidArgumentException::class);
120134
new Nim('210001143');
121135
}
122136
}

tests/PDDiktiTest.php

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
<?php
2+
3+
use GuzzleHttp\Client;
4+
use GuzzleHttp\Exception\RequestException;
5+
use GuzzleHttp\Handler\MockHandler;
6+
use GuzzleHttp\HandlerStack;
7+
use GuzzleHttp\Psr7\Request;
8+
use GuzzleHttp\Psr7\Response;
9+
use PHPUnit\Framework\TestCase;
10+
use Wastukancana\PDDikti;
11+
12+
final class PDDiktiTest extends TestCase
13+
{
14+
private const BASE_URI = 'https://api-pddikti.kemdiktisaintek.go.id';
15+
16+
private function injectClient(PDDikti $service, Client $client): void
17+
{
18+
$ref = new ReflectionClass(PDDikti::class);
19+
$prop = $ref->getProperty('http');
20+
21+
$prop->setAccessible(true);
22+
$prop->setValue($service, $client);
23+
}
24+
25+
private function makeClientFromMock(MockHandler $mock): Client
26+
{
27+
$handler = HandlerStack::create($mock);
28+
29+
return new Client([
30+
'handler' => $handler,
31+
'base_uri' => self::BASE_URI,
32+
'verify' => false,
33+
]);
34+
}
35+
36+
public function test_fetch_data_handles_exception_on_options(): void
37+
{
38+
$mock = new MockHandler([
39+
function () {
40+
throw new RequestException(
41+
'network failure',
42+
new Request('OPTIONS', self::BASE_URI.'/pencarian/all/211351143')
43+
);
44+
},
45+
]);
46+
47+
$client = $this->makeClientFromMock($mock);
48+
$svc = new PDDikti('211351143');
49+
$this->injectClient($svc, $client);
50+
51+
$this->assertNull($svc->getName());
52+
}
53+
54+
public function test_prepare_list_handles_empty_mahasiswa(): void
55+
{
56+
$mock = new MockHandler([
57+
new Response(204),
58+
new Response(200, ['Content-Type' => 'application/json'], json_encode(['mahasiswa' => []])),
59+
]);
60+
61+
$client = $this->makeClientFromMock($mock);
62+
$svc = new PDDikti('211351143');
63+
$this->injectClient($svc, $client);
64+
65+
$this->assertNull($svc->getName());
66+
}
67+
68+
public function test_prepare_detail_handles_empty_id(): void
69+
{
70+
$mock = new MockHandler([
71+
new Response(204),
72+
new Response(200, ['Content-Type' => 'application/json'], json_encode([
73+
'mahasiswa' => [(object) ['sinkatan_pt' => 'OTHER PT']],
74+
])),
75+
]);
76+
77+
$client = $this->makeClientFromMock($mock);
78+
$svc = new PDDikti('211351143');
79+
$this->injectClient($svc, $client);
80+
81+
$this->assertNull($svc->getGender());
82+
}
83+
84+
public function test_prepare_detail_handles_empty_data(): void
85+
{
86+
$mock = new MockHandler([
87+
new Response(204),
88+
new Response(200, ['Content-Type' => 'application/json'], json_encode([
89+
'mahasiswa' => [(object) ['sinkatan_pt' => 'STT WASTUKANCANA', 'id' => 'ID123', 'nama' => 'Foo']],
90+
])),
91+
new Response(204),
92+
function () {
93+
throw new RequestException('network failure', new Request('GET', self::BASE_URI.'/detail/mhs/ID123'));
94+
},
95+
]);
96+
97+
$client = $this->makeClientFromMock($mock);
98+
$svc = new PDDikti('211351143');
99+
$this->injectClient($svc, $client);
100+
101+
$this->assertNull($svc->getGender());
102+
$this->assertNull($svc->getIsGraduated());
103+
}
104+
}

0 commit comments

Comments
 (0)