Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions bootstrap/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
api: __DIR__.'/../routes/api.php',
apiPrefix: '', // Remove /api/ prefix - routes accessible at /v1/* directly
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
Expand Down
26 changes: 13 additions & 13 deletions tests/Feature/Auth/PasswordResetRequestTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
/**
* Feature tests for password reset request endpoint.
*
* @covers POST /api/v1/auth/password/reset-request
* @covers POST /v1/auth/password/reset-request
*/
uses(RefreshDatabase::class);

Expand All @@ -25,7 +25,7 @@
'email' => 'test@example.com',
]);

$response = $this->postJson('/api/v1/auth/password/reset-request', [
$response = $this->postJson('/v1/auth/password/reset-request', [
'email' => 'test@example.com',
]);

Expand All @@ -43,7 +43,7 @@
it('returns same response for non-existent email', function () {
Mail::fake();

$response = $this->postJson('/api/v1/auth/password/reset-request', [
$response = $this->postJson('/v1/auth/password/reset-request', [
'email' => 'nonexistent@example.com',
]);

Expand All @@ -57,14 +57,14 @@
});

it('requires email field', function () {
$response = $this->postJson('/api/v1/auth/password/reset-request', []);
$response = $this->postJson('/v1/auth/password/reset-request', []);

$response->assertStatus(422)
->assertJsonValidationErrors(['email']);
});

it('requires valid email format', function () {
$response = $this->postJson('/api/v1/auth/password/reset-request', [
$response = $this->postJson('/v1/auth/password/reset-request', [
'email' => 'invalid-email',
]);

Expand All @@ -76,12 +76,12 @@
$email = 'test@example.com';

// Make 5 requests (should all be allowed)
collect(range(1, 5))->each(fn () => $this->postJson('/api/v1/auth/password/reset-request', [
collect(range(1, 5))->each(fn () => $this->postJson('/v1/auth/password/reset-request', [
'email' => $email,
])->assertOk());

// 6th request should be rate limited
$response = $this->postJson('/api/v1/auth/password/reset-request', [
$response = $this->postJson('/v1/auth/password/reset-request', [
'email' => $email,
]);

Expand All @@ -95,7 +95,7 @@
'email' => 'test@example.com',
]);

$this->postJson('/api/v1/auth/password/reset-request', [
$this->postJson('/v1/auth/password/reset-request', [
'email' => 'test@example.com',
]);

Expand All @@ -113,7 +113,7 @@
'email' => 'test@example.com',
]);

$this->postJson('/api/v1/auth/password/reset-request', [
$this->postJson('/v1/auth/password/reset-request', [
'email' => 'test@example.com',
]);

Expand All @@ -132,7 +132,7 @@
'email' => 'test+special@example.com',
]);

$this->postJson('/api/v1/auth/password/reset-request', [
$this->postJson('/v1/auth/password/reset-request', [
'email' => 'test+special@example.com',
]);

Expand All @@ -159,7 +159,7 @@
'email' => 'test@example.com',
]);

$this->postJson('/api/v1/auth/password/reset-request', [
$this->postJson('/v1/auth/password/reset-request', [
'email' => 'test@example.com',
]);

Expand All @@ -183,7 +183,7 @@
]);

// Request password reset first time
$this->postJson('/api/v1/auth/password/reset-request', [
$this->postJson('/v1/auth/password/reset-request', [
'email' => 'test@example.com',
]);

Expand All @@ -194,7 +194,7 @@
expect($firstTokenCount)->toBe(1);

// Request again - should replace old token
$this->postJson('/api/v1/auth/password/reset-request', [
$this->postJson('/v1/auth/password/reset-request', [
'email' => 'test@example.com',
]);

Expand Down
22 changes: 11 additions & 11 deletions tests/Feature/Auth/PasswordResetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
/**
* Feature tests for password reset confirmation endpoint.
*
* @covers POST /api/v1/auth/password/reset
* @covers POST /v1/auth/password/reset
*/
uses(RefreshDatabase::class);

Expand Down Expand Up @@ -42,7 +42,7 @@ function createPasswordResetToken(User $user, ?\DateTimeInterface $createdAt = n

$token = createPasswordResetToken($user);

$response = $this->postJson('/api/v1/auth/password/reset', [
$response = $this->postJson('/v1/auth/password/reset', [
'token' => $token,
'email' => 'test@example.com',
'password' => 'new-secure-password-123',
Expand All @@ -65,7 +65,7 @@ function createPasswordResetToken(User $user, ?\DateTimeInterface $createdAt = n

$expiredToken = createPasswordResetToken($user, now()->subMinutes(61));

$response = $this->postJson('/api/v1/auth/password/reset', [
$response = $this->postJson('/v1/auth/password/reset', [
'token' => $expiredToken,
'email' => 'test@example.com',
'password' => 'new-password-123',
Expand All @@ -83,7 +83,7 @@ function createPasswordResetToken(User $user, ?\DateTimeInterface $createdAt = n
'email' => 'test@example.com',
]);

$response = $this->postJson('/api/v1/auth/password/reset', [
$response = $this->postJson('/v1/auth/password/reset', [
'token' => 'invalid-token-123',
'email' => 'test@example.com',
'password' => 'new-password-123',
Expand All @@ -97,14 +97,14 @@ function createPasswordResetToken(User $user, ?\DateTimeInterface $createdAt = n
});

it('requires all fields', function () {
$response = $this->postJson('/api/v1/auth/password/reset', []);
$response = $this->postJson('/v1/auth/password/reset', []);

$response->assertStatus(422)
->assertJsonValidationErrors(['token', 'email', 'password']);
});

it('requires password confirmation', function () {
$response = $this->postJson('/api/v1/auth/password/reset', [
$response = $this->postJson('/v1/auth/password/reset', [
'token' => 'some-token',
'email' => 'test@example.com',
'password' => 'new-password-123',
Expand All @@ -121,7 +121,7 @@ function createPasswordResetToken(User $user, ?\DateTimeInterface $createdAt = n

$token = createPasswordResetToken($user);

$response = $this->postJson('/api/v1/auth/password/reset', [
$response = $this->postJson('/v1/auth/password/reset', [
'token' => $token,
'email' => 'test@example.com',
'password' => 'short',
Expand All @@ -140,15 +140,15 @@ function createPasswordResetToken(User $user, ?\DateTimeInterface $createdAt = n
$token = createPasswordResetToken($user);

// First reset succeeds
$this->postJson('/api/v1/auth/password/reset', [
$this->postJson('/v1/auth/password/reset', [
'token' => $token,
'email' => 'test@example.com',
'password' => 'new-password-123',
'password_confirmation' => 'new-password-123',
])->assertOk();

// Second attempt with same token fails
$response = $this->postJson('/api/v1/auth/password/reset', [
$response = $this->postJson('/v1/auth/password/reset', [
'token' => $token,
'email' => 'test@example.com',
'password' => 'another-password-456',
Expand All @@ -167,15 +167,15 @@ function createPasswordResetToken(User $user, ?\DateTimeInterface $createdAt = n
]);

// Make 5 requests (should all be allowed)
collect(range(1, 5))->each(fn () => $this->postJson('/api/v1/auth/password/reset', [
collect(range(1, 5))->each(fn () => $this->postJson('/v1/auth/password/reset', [
'token' => 'wrong-token',
'email' => 'test@example.com',
'password' => 'new-password-123',
'password_confirmation' => 'new-password-123',
])->assertStatus(400)); // Wrong token, but not rate limited

// 6th request should be rate limited
$response = $this->postJson('/api/v1/auth/password/reset', [
$response = $this->postJson('/v1/auth/password/reset', [
'token' => 'wrong-token',
'email' => 'test@example.com',
'password' => 'new-password-123',
Expand Down
38 changes: 19 additions & 19 deletions tests/Feature/AuthTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
'password' => bcrypt('password123'),
]);

$response = $this->postJson('/api/v1/auth/token', [
$response = $this->postJson('/v1/auth/token', [
'email' => 'test@example.com',
'password' => 'password123',
'device_name' => 'test-device',
Expand All @@ -32,7 +32,7 @@
});

test('token generation fails with invalid email', function () {
$response = $this->postJson('/api/v1/auth/token', [
$response = $this->postJson('/v1/auth/token', [
'email' => 'nonexistent@example.com',
'password' => 'password123',
]);
Expand All @@ -47,7 +47,7 @@
'password' => bcrypt('correct-password'),
]);

$response = $this->postJson('/api/v1/auth/token', [
$response = $this->postJson('/v1/auth/token', [
'email' => 'test@example.com',
'password' => 'wrong-password',
]);
Expand All @@ -57,7 +57,7 @@
});

test('token generation requires email', function () {
$response = $this->postJson('/api/v1/auth/token', [
$response = $this->postJson('/v1/auth/token', [
'password' => 'password123',
]);

Expand All @@ -66,7 +66,7 @@
});

test('token generation requires password', function () {
$response = $this->postJson('/api/v1/auth/token', [
$response = $this->postJson('/v1/auth/token', [
'email' => 'test@example.com',
]);

Expand All @@ -80,7 +80,7 @@
'password' => bcrypt('password123'),
]);

$response = $this->postJson('/api/v1/auth/token', [
$response = $this->postJson('/v1/auth/token', [
'email' => 'test@example.com',
'password' => 'password123',
]);
Expand All @@ -95,13 +95,13 @@
'password' => bcrypt('password123'),
]);

$this->postJson('/api/v1/auth/token', [
$this->postJson('/v1/auth/token', [
'email' => 'test@example.com',
'password' => 'password123',
'device_name' => 'mobile',
])->assertCreated();

$this->postJson('/api/v1/auth/token', [
$this->postJson('/v1/auth/token', [
'email' => 'test@example.com',
'password' => 'password123',
'device_name' => 'desktop',
Expand All @@ -114,7 +114,7 @@

describe('Protected Endpoints', function () {
test('protected endpoint requires authentication', function () {
$response = $this->getJson('/api/v1/me');
$response = $this->getJson('/v1/me');

$response->assertUnauthorized();
});
Expand All @@ -128,7 +128,7 @@
$token = $user->createToken('test-device')->plainTextToken;

$response = $this->withHeader('Authorization', "Bearer {$token}")
->getJson('/api/v1/me');
->getJson('/v1/me');

$response->assertOk()
->assertJson([
Expand All @@ -140,7 +140,7 @@

test('protected endpoint rejects invalid token', function () {
$response = $this->withHeader('Authorization', 'Bearer invalid-token-here')
->getJson('/api/v1/me');
->getJson('/v1/me');

$response->assertUnauthorized();
});
Expand All @@ -156,7 +156,7 @@
$token = $user->createToken('device-1')->plainTextToken;

$response = $this->withHeader('Authorization', "Bearer {$token}")
->postJson('/api/v1/auth/logout');
->postJson('/v1/auth/logout');

$response->assertOk()
->assertJson(['message' => 'Token revoked successfully.']);
Expand All @@ -177,7 +177,7 @@
expect($user->tokens()->count())->toBe(3);

$response = $this->withHeader('Authorization', "Bearer {$token1}")
->postJson('/api/v1/auth/logout-all');
->postJson('/v1/auth/logout-all');

$response->assertOk()
->assertJson(['message' => 'All tokens revoked successfully.']);
Expand All @@ -194,21 +194,21 @@

// Logout (revoke token)
$this->withHeader('Authorization', "Bearer {$token}")
->postJson('/api/v1/auth/logout')
->postJson('/v1/auth/logout')
->assertOk();

// Token deleted after logout
expect($user->fresh()->tokens()->count())->toBe(0);
});

test('logout requires authentication', function () {
$response = $this->postJson('/api/v1/auth/logout');
$response = $this->postJson('/v1/auth/logout');

$response->assertUnauthorized();
});

test('logout-all requires authentication', function () {
$response = $this->postJson('/api/v1/auth/logout-all');
$response = $this->postJson('/v1/auth/logout-all');

$response->assertUnauthorized();
});
Expand All @@ -220,7 +220,7 @@
'password' => bcrypt('secret-password'),
]);

$response = $this->postJson('/api/v1/auth/token', [
$response = $this->postJson('/v1/auth/token', [
'email' => $user->email,
'password' => 'secret-password',
]);
Expand All @@ -235,7 +235,7 @@
$token = $user->createToken('test')->plainTextToken;

$response = $this->withHeader('Authorization', "Bearer {$token}")
->getJson('/api/v1/me');
->getJson('/v1/me');

$response->assertOk()
->assertJsonMissing(['password'])
Expand All @@ -248,7 +248,7 @@
'password' => bcrypt('password123'),
]);

$response = $this->postJson('/api/v1/auth/token', [
$response = $this->postJson('/v1/auth/token', [
'email' => 'test@example.com',
'password' => 'password123',
]);
Expand Down
2 changes: 1 addition & 1 deletion tests/Feature/ExampleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ExampleTest extends TestCase
*/
public function test_health_endpoint_returns_ok(): void
{
$response = $this->get('/api/health');
$response = $this->get('/health');

$response->assertStatus(200)
->assertJson([
Expand Down
Loading