count(2)->state([ 'user_id' => $result['user']->id, 'workspace_id' => $result['workspace']->id, 'revoked' => false, ])->create(); $response = $this->withHeaders([ 'Authorization' => 'Bearer '.$result['plain_token'], ])->getJson( route('api.api-keys.index'), ['HTTP_HOST' => 'api.trypost.test'] ); $response->assertOk(); // 1 from auth + 2 created + ? we may also need to ensure factory has client id $response->assertJsonCount(3); })->skip('AccessToken factory not available; covered by app-level ApiKeyControllerTest.'); test('create api key returns plain token', function () { $result = createApiKeyApiToken(); $response = $this->withHeaders([ 'Authorization' => 'Bearer '.$result['plain_token'], ])->postJson( route('api.api-keys.store'), ['name' => 'CI/CD Token'], ['HTTP_HOST' => 'api.trypost.test'] ); $response->assertCreated(); $response->assertJsonStructure([ 'token' => ['id', 'name', 'created_at'], 'plain_token', ]); expect($response->json('plain_token'))->toBeString(); }); test('create api key validation errors', function () { $result = createApiKeyApiToken(); $response = $this->withHeaders([ 'Authorization' => 'Bearer '.$result['plain_token'], ])->postJson( route('api.api-keys.store'), [], ['HTTP_HOST' => 'api.trypost.test'] ); $response->assertUnprocessable(); $response->assertJsonValidationErrors(['name']); }); test('delete api key', function () { $result = createApiKeyApiToken(); $tokenToDelete = $result['user']->createToken('To delete')->token; AccessToken::find($tokenToDelete->id) ->forceFill(['workspace_id' => $result['workspace']->id]) ->saveQuietly(); $response = $this->withHeaders([ 'Authorization' => 'Bearer '.$result['plain_token'], ])->deleteJson( route('api.api-keys.destroy', $tokenToDelete->id), [], ['HTTP_HOST' => 'api.trypost.test'] ); $response->assertNoContent(); expect(AccessToken::find($tokenToDelete->id)->revoked)->toBeTrue(); }); test('cannot delete api key from another workspace', function () { $result = createApiKeyApiToken(); $otherWorkspace = Workspace::factory()->create(); $otherUser = $otherWorkspace->owner ?? User::factory()->create([ 'account_id' => $otherWorkspace->account_id, ]); $otherToken = $otherUser->createToken('Other')->token; AccessToken::find($otherToken->id) ->forceFill(['workspace_id' => $otherWorkspace->id]) ->saveQuietly(); $response = $this->withHeaders([ 'Authorization' => 'Bearer '.$result['plain_token'], ])->deleteJson( route('api.api-keys.destroy', $otherToken->id), [], ['HTTP_HOST' => 'api.trypost.test'] ); $response->assertNotFound(); }); it('validates api key expires_at must be future date', function () { $result = createApiKeyApiToken(); $this->withHeaders(['Authorization' => 'Bearer '.$result['plain_token']]) ->postJson(route('api.api-keys.store'), [ 'name' => 'Test Key', 'expires_at' => '2020-01-01', ]) ->assertUnprocessable() ->assertJsonValidationErrors(['expires_at']); }); it('validates api key name max length', function () { $result = createApiKeyApiToken(); $this->withHeaders(['Authorization' => 'Bearer '.$result['plain_token']]) ->postJson(route('api.api-keys.store'), [ 'name' => str_repeat('a', 256), ]) ->assertUnprocessable() ->assertJsonValidationErrors(['name']); });