Skip to content

Feat: Tambahkan unit & integration tests untuk lifecycle API key#1684

Open
pandigresik wants to merge 3 commits into
rilis-devfrom
feat/api-test-lifecycle
Open

Feat: Tambahkan unit & integration tests untuk lifecycle API key#1684
pandigresik wants to merge 3 commits into
rilis-devfrom
feat/api-test-lifecycle

Conversation

@pandigresik

Copy link
Copy Markdown
Contributor

Feat: Tambahkan unit & integration tests untuk lifecycle API key (#1675)

Description

Menambahkan unit tests dan integration tests di OpenDK yang men-cover seluruh lifecycle API key: pembuatan, validasi, revocation/disable, scope enforcement, dan audit logging. Tests menggunakan SQLite in-memory sehingga dapat dijalankan tanpa dependensi MySQL, menjadikannya cocok untuk CI.

Changes made:

  1. Feature - Model: Menambahkan ApiKey model dengan status constants (active/revoked/disabled/expired), helper methods (isActive, isExpired, hasScope), dan key generation utilities.
  2. Feature - Model: Menambahkan ApiKeyAuditLog model untuk mencatat setiap operasi API key (created, validated, revoked, disabled).
  3. Feature - Service: Menambahkan KeyService yang mengelola lifecycle API key: create, validate (dengan scope check), revoke, disable, enable, dan audit logging.
  4. Feature - Controller: Menambahkan ApiKeyController dengan endpoint CRUD (index, store, show, revoke, destroy) yang menggunakan StoreApiKeyRequest untuk validasi.
  5. Feature - Middleware: Menambahkan ApiKeyMiddleware yang memvalidasi API key dari bearer token, mendukung optional scope parameter, dan mengembalikan status code yang sesuai (401/403).
  6. Feature - Form Request: Menambahkan StoreApiKeyRequest untuk validasi input pembuatan API key (name required, scopes optional array, expires_at optional date).
  7. Feature - Routes: Menambahkan route group api-keys dengan middleware auth:api (JWT auth tanpa token.registered), dan route key/validate dengan middleware api.key.
  8. Feature - Migration: Menambahkan migration api_keys dan api_key_audit_logs dengan foreign key ke users menggunakan unsignedInteger (compatibility dengan struktur users.id existing).
  9. Feature - Factory: Menambahkan ApiKeyFactory (dengan states: revoked, disabled, expired, withScope, withRawKey) dan SamplePayloadFactory (dengan state: penduduk).
  10. Feature - Tests: Menambahkan 18 integration tests (ApiKeyIntegrationTest) yang menguji controller + middleware + database menggunakan SQLite in-memory.
  11. Feature - Tests: Menambahkan 18 unit tests (KeyServiceTest) yang menguji KeyService langsung (create, validate, revoke, disable/enable, scope, audit log).
  12. Feature - Test Infrastructure: Menambahkan ApiKeyTestCase base class yang force SQLite in-memory sebelum app boot, dan WithApiKeyTesting trait yang membuat schema SQLite-compatible.
  13. Refactor - Config: Membersihkan phpunit.xml dari atribut deprecated (beStrictAboutTodoAnnotatedTests, verbose, processUncoveredFiles, crap4j, phpcov) untuk kompatibilitas dengan PHPUnit 12.
  14. Refactor - Config: Meregister middleware api.key di bootstrap/app.php.

Reason for change:

  • Point 1: API key functionality belum memiliki test coverage, sehingga perubahan pada service/middleware dapat mengakibatkan regression yang tidak terdeteksi.
  • Point 2: Tests yang ada di project menggunakan MySQL, sehingga membutuhkan database server untuk menjalankannya. SQLite in-memory memungkinkan tests berjalan tanpa dependensi external.
  • Point 3: Negative test cases (missing key, invalid key, revoked key, insufficient scope) belum ter-cover, padahal ini adalah edge cases penting untuk keamanan API.

Impact of change:

Test Coverage: Module API key memiliki minimal 80% test coverage dengan 36 tests (101 assertions) yang menjalankan seluruh lifecycle.
CI Compatibility: Tests dapat dijalankan dengan php artisan test --testsuite=ApiKey tanpa MySQL, sehingga lebih cepat dan reliable di CI.
Code Quality: Penggunaan FormRequest untuk validasi, middleware untuk autentikasi, dan Service layer untuk business logic menjadikan kode lebih terstruktur dan maintainable.
Negative Cases: Edge cases seperti revoked key, disabled key, insufficient scope, dan idempotency sudah ter-cover dalam tests.

Related Issue

  • Solution for feature related to issue #1675

Steps to Reproduce

Before (no tests):

  1. Tidak ada tests untuk API key module
  2. Perubahan pada KeyService/ApiKeyMiddleware tidak terdeteksi oleh automated tests
  3. Tidak ada negative test cases untuk edge cases keamanan

After (with tests):

  1. Jalankan php artisan test --testsuite=ApiKey
  2. ✅ 36 tests passed (101 assertions)
  3. ✅ Seluruh lifecycle ter-cover: create → validate → revoke/disable → audit log
  4. ✅ Negative cases ter-cover: missing key, invalid key, revoked key, insufficient scope, idempotency

Testing on related features:

  • KeyService create/validate/revoke ✅ Passed
  • ApiKeyMiddleware auth/scope handling ✅ Passed
  • ApiKeyController CRUD operations ✅ Passed
  • Audit logging per request ✅ Passed

Checklist

  • Unit tests (Pest) untuk KeyService (create, revoke, validate) - 18 tests
  • Integration tests untuk controller + middleware + database (SQLite in-memory) - 18 tests
  • Factories untuk test fixtures (ApiKeyFactory, SamplePayloadFactory)
  • Negative tests: missing key, invalid key, revoked key, insufficient scope
  • Idempotency/duplicate submission test
  • Test teardown/DB rollback via transaction
  • Tests run reliably in CI (SQLite in-memory)
  • phpunit.xml cleaned from deprecated attributes for PHPUnit 12

Technical Details

Technical Explanation

API Key Lifecycle Flow:

User (JWT) → ApiKeyController → KeyService::create() → DB (api_keys)
                                                          ↓
                                                     Audit Log (created)

External Client → ApiKeyMiddleware → KeyService::validate() → Scope Check → DB (last_used_at)
                                                                              ↓
                                                                         Audit Log (validate.success)

User (JWT) → ApiKeyController → KeyService::revoke() → DB (status=revoked)
                                                          ↓
                                                     Audit Log (revoked)

Test Architecture:

  • ApiKeyTestCase extends BaseTestCase dengan CreatesApplication, WithApiKeyTesting, WithSettingAplikasi
  • setUpBeforeClass() force $_ENV['DB_CONNECTION'] = 'sqlite' sebelum app boot untuk override .env.testing yang menggunakan MySQL
  • WithApiKeyTesting trait membuat schema SQLite-compatible (tanpa ALTER TABLE CHANGE yang tidak didukung SQLite)
  • Setiap test dijalankan dalam transaction yang di-rollback di tearDown()

Key Design Decisions:

  • users.id di project ini menggunakan int unsigned (32-bit), sehingga FK ke users harus menggunakan unsignedInteger bukan foreignId (yang default unsignedBigInteger)
  • Route api-keys hanya membutuhkan auth:api middleware (tanpa token.registered) karena management API keys adalah operasi autentikasi itu sendiri
  • KeyService::validate() menggunakan Hash::check() untuk membandingkan raw key dengan hashed key di database, sehingga raw key tidak pernah disimpan

Configuration changes

  • phpunit.xml: Hapus atribut deprecated (beStrictAboutTodoAnnotatedTests, verbose, processUncoveredFiles, crap4j, phpcov) dan tambahkan testsuite ApiKey
  • bootstrap/app.php: Register middleware api.keyApiKeyMiddleware::class
  • routes/api.php: Tambahkan route group api-keys (dengan auth:api) dan key/validate (dengan api.key)

Dependencies added

No new dependencies.

Testing

Automated Testing

  • Unit Test - KeyServiceTest: 18 tests (create, validate, revoke, disable/enable, scope, audit log, idempotency, expired key)
  • Integration Test - ApiKeyIntegrationTest: 18 tests (controller CRUD, middleware auth/scope, negative cases, audit log)
  • Total: 36 tests, 101 assertions

Manual Testing

  • Jalankan php artisan migrate untuk membuat tabel api_keys dan api_key_audit_logs
  • Login ke aplikasi dan buat API key melalui API endpoint
  • Gunakan API key yang dibuat untuk mengakses endpoint yang dilindungi middleware api.key
  • Coba akses dengan revoked key (harus 403)
  • Coba akses tanpa key (harus 401)
  • Coba akses dengan scope yang tidak mencukupi (harus 403)

Breaking Changes

None

Migration Guide

Jalankan php artisan migrate untuk membuat tabel baru:

  • api_keys: Menyimpan data API key (hashed) milik setiap user
  • api_key_audit_logs: Mencatat setiap operasi API key (create, validate, revoke, disable)

References


Additional notes:

  • Tests menggunakan SQLite in-memory sehingga tidak membutuhkan MySQL server
  • SamplePayloadFactory disediakan untuk test data payload API, dapat digunakan untuk integration testing lebih lanjut
  • WithApiKeyTesting trait dapat direuse untuk test lain yang membutuhkan schema users/das_setting di SQLite

@pandigresik
pandigresik requested a review from vickyrolanda July 21, 2026 02:10
@github-actions

Copy link
Copy Markdown
Contributor

🔄 AI PR Review sedang antri di server...

Proses review akan segera dimulai di background — hasil akan muncul sebagai komentar setelah selesai.
Powered by CrewAI · PR #1684

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant