P2 Items Completed (P2-062 to P2-068): - Switch AgentApiKey from SHA-256 to Argon2id hashing - Add 200+ tests for models, services, and AI providers - Create agent_plans migration with phases and workspace states Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
961 lines
27 KiB
PHP
961 lines
27 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Core\Mod\Agentic\Tests\Feature;
|
|
|
|
use Carbon\Carbon;
|
|
use Core\Mod\Agentic\Models\AgentApiKey;
|
|
use Core\Tenant\Models\Workspace;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* Tests for the AgentApiKey model.
|
|
*
|
|
* Covers generation, validation, permissions, rate limiting, and IP restrictions.
|
|
*/
|
|
class AgentApiKeyTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
private Workspace $workspace;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->workspace = Workspace::factory()->create();
|
|
}
|
|
|
|
// =========================================================================
|
|
// Key Generation Tests
|
|
// =========================================================================
|
|
|
|
public function test_it_generates_key_with_correct_prefix(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertStringStartsWith('ak_', $key->plainTextKey);
|
|
$this->assertEquals(35, strlen($key->plainTextKey)); // ak_ + 32 random chars
|
|
}
|
|
|
|
public function test_it_stores_hashed_key_with_argon2id(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
// Argon2id hashes start with $argon2id$
|
|
$this->assertStringStartsWith('$argon2id$', $key->key);
|
|
}
|
|
|
|
public function test_plaintext_key_is_only_available_once(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$plainKey = $key->plainTextKey;
|
|
$this->assertNotNull($plainKey);
|
|
|
|
// After fetching from database, plaintext should be null
|
|
$freshKey = AgentApiKey::find($key->id);
|
|
$this->assertNull($freshKey->plainTextKey);
|
|
}
|
|
|
|
public function test_it_generates_key_with_workspace_id(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace->id,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertEquals($this->workspace->id, $key->workspace_id);
|
|
}
|
|
|
|
public function test_it_generates_key_with_workspace_model(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertEquals($this->workspace->id, $key->workspace_id);
|
|
}
|
|
|
|
public function test_it_generates_key_with_permissions(): void
|
|
{
|
|
$permissions = [
|
|
AgentApiKey::PERM_PLANS_READ,
|
|
AgentApiKey::PERM_PLANS_WRITE,
|
|
];
|
|
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
$permissions
|
|
);
|
|
|
|
$this->assertEquals($permissions, $key->permissions);
|
|
}
|
|
|
|
public function test_it_generates_key_with_custom_rate_limit(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
500
|
|
);
|
|
|
|
$this->assertEquals(500, $key->rate_limit);
|
|
}
|
|
|
|
public function test_it_generates_key_with_expiry(): void
|
|
{
|
|
$expiresAt = Carbon::now()->addDays(30);
|
|
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
$expiresAt
|
|
);
|
|
|
|
$this->assertEquals($expiresAt->toDateTimeString(), $key->expires_at->toDateTimeString());
|
|
}
|
|
|
|
public function test_it_initialises_call_count_to_zero(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertEquals(0, $key->call_count);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Key Lookup Tests
|
|
// =========================================================================
|
|
|
|
public function test_find_by_key_returns_correct_key(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$found = AgentApiKey::findByKey($plainKey);
|
|
|
|
$this->assertNotNull($found);
|
|
$this->assertEquals($key->id, $found->id);
|
|
}
|
|
|
|
public function test_find_by_key_returns_null_for_invalid_key(): void
|
|
{
|
|
AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$found = AgentApiKey::findByKey('ak_invalid_key_that_does_not_exist');
|
|
|
|
$this->assertNull($found);
|
|
}
|
|
|
|
public function test_find_by_key_returns_null_for_malformed_key(): void
|
|
{
|
|
$this->assertNull(AgentApiKey::findByKey(''));
|
|
$this->assertNull(AgentApiKey::findByKey('invalid'));
|
|
$this->assertNull(AgentApiKey::findByKey('ak_short'));
|
|
}
|
|
|
|
public function test_find_by_key_does_not_find_revoked_keys(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$key->revoke();
|
|
|
|
$found = AgentApiKey::findByKey($plainKey);
|
|
|
|
$this->assertNull($found);
|
|
}
|
|
|
|
public function test_find_by_key_does_not_find_expired_keys(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->subDay()
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$found = AgentApiKey::findByKey($plainKey);
|
|
|
|
$this->assertNull($found);
|
|
}
|
|
|
|
public function test_verify_key_returns_true_for_matching_key(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
$plainKey = $key->plainTextKey;
|
|
|
|
$this->assertTrue($key->verifyKey($plainKey));
|
|
}
|
|
|
|
public function test_verify_key_returns_false_for_non_matching_key(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertFalse($key->verifyKey('ak_wrong_key_entirely'));
|
|
}
|
|
|
|
// =========================================================================
|
|
// Status Tests
|
|
// =========================================================================
|
|
|
|
public function test_is_active_returns_true_for_fresh_key(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertTrue($key->isActive());
|
|
}
|
|
|
|
public function test_is_active_returns_false_for_revoked_key(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
$key->revoke();
|
|
|
|
$this->assertFalse($key->isActive());
|
|
}
|
|
|
|
public function test_is_active_returns_false_for_expired_key(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->subDay()
|
|
);
|
|
|
|
$this->assertFalse($key->isActive());
|
|
}
|
|
|
|
public function test_is_active_returns_true_for_key_with_future_expiry(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->addDay()
|
|
);
|
|
|
|
$this->assertTrue($key->isActive());
|
|
}
|
|
|
|
public function test_is_revoked_returns_correct_value(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertFalse($key->isRevoked());
|
|
|
|
$key->revoke();
|
|
|
|
$this->assertTrue($key->isRevoked());
|
|
}
|
|
|
|
public function test_is_expired_returns_correct_value(): void
|
|
{
|
|
$notExpired = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Not Expired',
|
|
[],
|
|
100,
|
|
Carbon::now()->addDay()
|
|
);
|
|
|
|
$expired = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Expired',
|
|
[],
|
|
100,
|
|
Carbon::now()->subDay()
|
|
);
|
|
|
|
$noExpiry = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'No Expiry'
|
|
);
|
|
|
|
$this->assertFalse($notExpired->isExpired());
|
|
$this->assertTrue($expired->isExpired());
|
|
$this->assertFalse($noExpiry->isExpired());
|
|
}
|
|
|
|
// =========================================================================
|
|
// Permission Tests
|
|
// =========================================================================
|
|
|
|
public function test_has_permission_returns_true_when_granted(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ, AgentApiKey::PERM_PLANS_WRITE]
|
|
);
|
|
|
|
$this->assertTrue($key->hasPermission(AgentApiKey::PERM_PLANS_READ));
|
|
$this->assertTrue($key->hasPermission(AgentApiKey::PERM_PLANS_WRITE));
|
|
}
|
|
|
|
public function test_has_permission_returns_false_when_not_granted(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
|
|
$this->assertFalse($key->hasPermission(AgentApiKey::PERM_PLANS_WRITE));
|
|
}
|
|
|
|
public function test_has_any_permission_returns_true_when_one_matches(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
|
|
$this->assertTrue($key->hasAnyPermission([
|
|
AgentApiKey::PERM_PLANS_READ,
|
|
AgentApiKey::PERM_PLANS_WRITE,
|
|
]));
|
|
}
|
|
|
|
public function test_has_any_permission_returns_false_when_none_match(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_TEMPLATES_READ]
|
|
);
|
|
|
|
$this->assertFalse($key->hasAnyPermission([
|
|
AgentApiKey::PERM_PLANS_READ,
|
|
AgentApiKey::PERM_PLANS_WRITE,
|
|
]));
|
|
}
|
|
|
|
public function test_has_all_permissions_returns_true_when_all_granted(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ, AgentApiKey::PERM_PLANS_WRITE, AgentApiKey::PERM_SESSIONS_READ]
|
|
);
|
|
|
|
$this->assertTrue($key->hasAllPermissions([
|
|
AgentApiKey::PERM_PLANS_READ,
|
|
AgentApiKey::PERM_PLANS_WRITE,
|
|
]));
|
|
}
|
|
|
|
public function test_has_all_permissions_returns_false_when_missing_one(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
|
|
$this->assertFalse($key->hasAllPermissions([
|
|
AgentApiKey::PERM_PLANS_READ,
|
|
AgentApiKey::PERM_PLANS_WRITE,
|
|
]));
|
|
}
|
|
|
|
public function test_update_permissions_changes_permissions(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
|
|
$key->updatePermissions([AgentApiKey::PERM_SESSIONS_WRITE]);
|
|
|
|
$this->assertFalse($key->hasPermission(AgentApiKey::PERM_PLANS_READ));
|
|
$this->assertTrue($key->hasPermission(AgentApiKey::PERM_SESSIONS_WRITE));
|
|
}
|
|
|
|
// =========================================================================
|
|
// Rate Limiting Tests
|
|
// =========================================================================
|
|
|
|
public function test_is_rate_limited_returns_false_when_under_limit(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100
|
|
);
|
|
|
|
Cache::put("agent_api_key_rate:{$key->id}", 50, 60);
|
|
|
|
$this->assertFalse($key->isRateLimited());
|
|
}
|
|
|
|
public function test_is_rate_limited_returns_true_when_at_limit(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100
|
|
);
|
|
|
|
Cache::put("agent_api_key_rate:{$key->id}", 100, 60);
|
|
|
|
$this->assertTrue($key->isRateLimited());
|
|
}
|
|
|
|
public function test_is_rate_limited_returns_true_when_over_limit(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100
|
|
);
|
|
|
|
Cache::put("agent_api_key_rate:{$key->id}", 150, 60);
|
|
|
|
$this->assertTrue($key->isRateLimited());
|
|
}
|
|
|
|
public function test_get_recent_call_count_returns_cache_value(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
Cache::put("agent_api_key_rate:{$key->id}", 42, 60);
|
|
|
|
$this->assertEquals(42, $key->getRecentCallCount());
|
|
}
|
|
|
|
public function test_get_recent_call_count_returns_zero_when_not_cached(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertEquals(0, $key->getRecentCallCount());
|
|
}
|
|
|
|
public function test_get_remaining_calls_returns_correct_value(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100
|
|
);
|
|
|
|
Cache::put("agent_api_key_rate:{$key->id}", 30, 60);
|
|
|
|
$this->assertEquals(70, $key->getRemainingCalls());
|
|
}
|
|
|
|
public function test_get_remaining_calls_returns_zero_when_over_limit(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100
|
|
);
|
|
|
|
Cache::put("agent_api_key_rate:{$key->id}", 150, 60);
|
|
|
|
$this->assertEquals(0, $key->getRemainingCalls());
|
|
}
|
|
|
|
public function test_update_rate_limit_changes_limit(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100
|
|
);
|
|
|
|
$key->updateRateLimit(200);
|
|
|
|
$this->assertEquals(200, $key->fresh()->rate_limit);
|
|
}
|
|
|
|
// =========================================================================
|
|
// IP Restriction Tests
|
|
// =========================================================================
|
|
|
|
public function test_ip_restrictions_disabled_by_default(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertFalse($key->ip_restriction_enabled);
|
|
}
|
|
|
|
public function test_enable_ip_restriction_sets_flag(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$key->enableIpRestriction();
|
|
|
|
$this->assertTrue($key->fresh()->ip_restriction_enabled);
|
|
}
|
|
|
|
public function test_disable_ip_restriction_clears_flag(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
$key->enableIpRestriction();
|
|
|
|
$key->disableIpRestriction();
|
|
|
|
$this->assertFalse($key->fresh()->ip_restriction_enabled);
|
|
}
|
|
|
|
public function test_update_ip_whitelist_sets_list(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$key->updateIpWhitelist(['192.168.1.1', '10.0.0.0/8']);
|
|
|
|
$this->assertEquals(['192.168.1.1', '10.0.0.0/8'], $key->fresh()->ip_whitelist);
|
|
}
|
|
|
|
public function test_add_to_ip_whitelist_adds_entry(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
$key->updateIpWhitelist(['192.168.1.1']);
|
|
|
|
$key->addToIpWhitelist('10.0.0.1');
|
|
|
|
$this->assertContains('192.168.1.1', $key->fresh()->ip_whitelist);
|
|
$this->assertContains('10.0.0.1', $key->fresh()->ip_whitelist);
|
|
}
|
|
|
|
public function test_add_to_ip_whitelist_does_not_duplicate(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
$key->updateIpWhitelist(['192.168.1.1']);
|
|
|
|
$key->addToIpWhitelist('192.168.1.1');
|
|
|
|
$this->assertCount(1, $key->fresh()->ip_whitelist);
|
|
}
|
|
|
|
public function test_remove_from_ip_whitelist_removes_entry(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
$key->updateIpWhitelist(['192.168.1.1', '10.0.0.1']);
|
|
|
|
$key->removeFromIpWhitelist('192.168.1.1');
|
|
|
|
$whitelist = $key->fresh()->ip_whitelist;
|
|
$this->assertNotContains('192.168.1.1', $whitelist);
|
|
$this->assertContains('10.0.0.1', $whitelist);
|
|
}
|
|
|
|
public function test_has_ip_restrictions_returns_correct_value(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
// No restrictions
|
|
$this->assertFalse($key->hasIpRestrictions());
|
|
|
|
// Enabled but no whitelist
|
|
$key->enableIpRestriction();
|
|
$this->assertFalse($key->fresh()->hasIpRestrictions());
|
|
|
|
// Enabled with whitelist
|
|
$key->updateIpWhitelist(['192.168.1.1']);
|
|
$this->assertTrue($key->fresh()->hasIpRestrictions());
|
|
}
|
|
|
|
public function test_get_ip_whitelist_count_returns_correct_value(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertEquals(0, $key->getIpWhitelistCount());
|
|
|
|
$key->updateIpWhitelist(['192.168.1.1', '10.0.0.0/8', '172.16.0.0/12']);
|
|
|
|
$this->assertEquals(3, $key->fresh()->getIpWhitelistCount());
|
|
}
|
|
|
|
public function test_record_last_used_ip_stores_ip(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$key->recordLastUsedIp('192.168.1.100');
|
|
|
|
$this->assertEquals('192.168.1.100', $key->fresh()->last_used_ip);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Actions Tests
|
|
// =========================================================================
|
|
|
|
public function test_revoke_sets_revoked_at(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$key->revoke();
|
|
|
|
$this->assertNotNull($key->fresh()->revoked_at);
|
|
}
|
|
|
|
public function test_record_usage_increments_count(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$key->recordUsage();
|
|
$key->recordUsage();
|
|
$key->recordUsage();
|
|
|
|
$this->assertEquals(3, $key->fresh()->call_count);
|
|
}
|
|
|
|
public function test_record_usage_updates_last_used_at(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertNull($key->last_used_at);
|
|
|
|
$key->recordUsage();
|
|
|
|
$this->assertNotNull($key->fresh()->last_used_at);
|
|
}
|
|
|
|
public function test_extend_expiry_updates_expiry(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->addDay()
|
|
);
|
|
|
|
$newExpiry = Carbon::now()->addMonth();
|
|
$key->extendExpiry($newExpiry);
|
|
|
|
$this->assertEquals(
|
|
$newExpiry->toDateTimeString(),
|
|
$key->fresh()->expires_at->toDateTimeString()
|
|
);
|
|
}
|
|
|
|
public function test_remove_expiry_clears_expiry(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->addDay()
|
|
);
|
|
|
|
$key->removeExpiry();
|
|
|
|
$this->assertNull($key->fresh()->expires_at);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Scope Tests
|
|
// =========================================================================
|
|
|
|
public function test_active_scope_filters_correctly(): void
|
|
{
|
|
AgentApiKey::generate($this->workspace, 'Active Key');
|
|
$revoked = AgentApiKey::generate($this->workspace, 'Revoked Key');
|
|
$revoked->revoke();
|
|
AgentApiKey::generate($this->workspace, 'Expired Key', [], 100, Carbon::now()->subDay());
|
|
|
|
$activeKeys = AgentApiKey::active()->get();
|
|
|
|
$this->assertCount(1, $activeKeys);
|
|
$this->assertEquals('Active Key', $activeKeys->first()->name);
|
|
}
|
|
|
|
public function test_for_workspace_scope_filters_correctly(): void
|
|
{
|
|
$otherWorkspace = Workspace::factory()->create();
|
|
|
|
AgentApiKey::generate($this->workspace, 'Our Key');
|
|
AgentApiKey::generate($otherWorkspace, 'Their Key');
|
|
|
|
$ourKeys = AgentApiKey::forWorkspace($this->workspace)->get();
|
|
|
|
$this->assertCount(1, $ourKeys);
|
|
$this->assertEquals('Our Key', $ourKeys->first()->name);
|
|
}
|
|
|
|
public function test_revoked_scope_filters_correctly(): void
|
|
{
|
|
AgentApiKey::generate($this->workspace, 'Active Key');
|
|
$revoked = AgentApiKey::generate($this->workspace, 'Revoked Key');
|
|
$revoked->revoke();
|
|
|
|
$revokedKeys = AgentApiKey::revoked()->get();
|
|
|
|
$this->assertCount(1, $revokedKeys);
|
|
$this->assertEquals('Revoked Key', $revokedKeys->first()->name);
|
|
}
|
|
|
|
public function test_expired_scope_filters_correctly(): void
|
|
{
|
|
AgentApiKey::generate($this->workspace, 'Active Key');
|
|
AgentApiKey::generate($this->workspace, 'Expired Key', [], 100, Carbon::now()->subDay());
|
|
|
|
$expiredKeys = AgentApiKey::expired()->get();
|
|
|
|
$this->assertCount(1, $expiredKeys);
|
|
$this->assertEquals('Expired Key', $expiredKeys->first()->name);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Display Helper Tests
|
|
// =========================================================================
|
|
|
|
public function test_get_masked_key_returns_masked_format(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$masked = $key->getMaskedKey();
|
|
|
|
$this->assertStringStartsWith('ak_', $masked);
|
|
$this->assertStringEndsWith('...', $masked);
|
|
}
|
|
|
|
public function test_get_status_label_returns_correct_label(): void
|
|
{
|
|
$active = AgentApiKey::generate($this->workspace, 'Active');
|
|
$revoked = AgentApiKey::generate($this->workspace, 'Revoked');
|
|
$revoked->revoke();
|
|
$expired = AgentApiKey::generate($this->workspace, 'Expired', [], 100, Carbon::now()->subDay());
|
|
|
|
$this->assertEquals('Active', $active->getStatusLabel());
|
|
$this->assertEquals('Revoked', $revoked->getStatusLabel());
|
|
$this->assertEquals('Expired', $expired->getStatusLabel());
|
|
}
|
|
|
|
public function test_get_status_colour_returns_correct_colour(): void
|
|
{
|
|
$active = AgentApiKey::generate($this->workspace, 'Active');
|
|
$revoked = AgentApiKey::generate($this->workspace, 'Revoked');
|
|
$revoked->revoke();
|
|
$expired = AgentApiKey::generate($this->workspace, 'Expired', [], 100, Carbon::now()->subDay());
|
|
|
|
$this->assertEquals('green', $active->getStatusColor());
|
|
$this->assertEquals('red', $revoked->getStatusColor());
|
|
$this->assertEquals('amber', $expired->getStatusColor());
|
|
}
|
|
|
|
public function test_get_last_used_for_humans_returns_never_when_null(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertEquals('Never', $key->getLastUsedForHumans());
|
|
}
|
|
|
|
public function test_get_last_used_for_humans_returns_diff_when_set(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
$key->update(['last_used_at' => Carbon::now()->subHour()]);
|
|
|
|
$this->assertStringContainsString('ago', $key->getLastUsedForHumans());
|
|
}
|
|
|
|
public function test_get_expires_for_humans_returns_never_when_null(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertEquals('Never', $key->getExpiresForHumans());
|
|
}
|
|
|
|
public function test_get_expires_for_humans_returns_expired_when_past(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->subDay()
|
|
);
|
|
|
|
$this->assertStringContainsString('Expired', $key->getExpiresForHumans());
|
|
}
|
|
|
|
public function test_get_expires_for_humans_returns_expires_when_future(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[],
|
|
100,
|
|
Carbon::now()->addDay()
|
|
);
|
|
|
|
$this->assertStringContainsString('Expires', $key->getExpiresForHumans());
|
|
}
|
|
|
|
// =========================================================================
|
|
// Array Output Tests
|
|
// =========================================================================
|
|
|
|
public function test_to_array_includes_expected_keys(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key',
|
|
[AgentApiKey::PERM_PLANS_READ]
|
|
);
|
|
|
|
$array = $key->toArray();
|
|
|
|
$this->assertArrayHasKey('id', $array);
|
|
$this->assertArrayHasKey('workspace_id', $array);
|
|
$this->assertArrayHasKey('name', $array);
|
|
$this->assertArrayHasKey('permissions', $array);
|
|
$this->assertArrayHasKey('rate_limit', $array);
|
|
$this->assertArrayHasKey('call_count', $array);
|
|
$this->assertArrayHasKey('status', $array);
|
|
$this->assertArrayHasKey('ip_restriction_enabled', $array);
|
|
$this->assertArrayHasKey('ip_whitelist_count', $array);
|
|
|
|
// Should NOT include the key hash
|
|
$this->assertArrayNotHasKey('key', $array);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Available Permissions Tests
|
|
// =========================================================================
|
|
|
|
public function test_available_permissions_returns_all_permissions(): void
|
|
{
|
|
$permissions = AgentApiKey::availablePermissions();
|
|
|
|
$this->assertIsArray($permissions);
|
|
$this->assertArrayHasKey(AgentApiKey::PERM_PLANS_READ, $permissions);
|
|
$this->assertArrayHasKey(AgentApiKey::PERM_PLANS_WRITE, $permissions);
|
|
$this->assertArrayHasKey(AgentApiKey::PERM_SESSIONS_READ, $permissions);
|
|
$this->assertArrayHasKey(AgentApiKey::PERM_SESSIONS_WRITE, $permissions);
|
|
$this->assertArrayHasKey(AgentApiKey::PERM_NOTIFY_READ, $permissions);
|
|
$this->assertArrayHasKey(AgentApiKey::PERM_NOTIFY_WRITE, $permissions);
|
|
$this->assertArrayHasKey(AgentApiKey::PERM_NOTIFY_SEND, $permissions);
|
|
}
|
|
|
|
// =========================================================================
|
|
// Relationship Tests
|
|
// =========================================================================
|
|
|
|
public function test_belongs_to_workspace(): void
|
|
{
|
|
$key = AgentApiKey::generate(
|
|
$this->workspace,
|
|
'Test Key'
|
|
);
|
|
|
|
$this->assertInstanceOf(Workspace::class, $key->workspace);
|
|
$this->assertEquals($this->workspace->id, $key->workspace->id);
|
|
}
|
|
}
|