Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
32 changes: 31 additions & 1 deletion backend/tests/apps/api/internal/mutations/api_key_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from datetime import timedelta
from datetime import datetime, timedelta
from unittest.mock import MagicMock, patch
from uuid import uuid4

Expand Down Expand Up @@ -56,6 +56,36 @@ def test_create_api_key_success(self, mock_api_key_create, api_key_mutations):
assert result.api_key == mock_instance
assert result.raw_key == raw_key

@patch("apps.api.internal.mutations.api_key.ApiKey.create")
@patch("apps.api.internal.mutations.api_key.timezone.now")
def test_create_api_key_end_of_day_is_valid(
self, mock_now, mock_api_key_create, api_key_mutations
):
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
):
"""Ensure an end-of-day expiry on the current date is accepted."""
info = mock_info()
user = info.context.request.user
fixed_now = datetime(2026, 1, 1, 12, 0, tzinfo=timezone.utc)

mock_now.return_value = fixed_now

expires_at = datetime(2026, 1, 1, 23, 59, 59, 999000, tzinfo=timezone.utc)

mock_instance = MagicMock(spec=ApiKey)
mock_api_key_create.return_value = (mock_instance, "raw_key")

result = api_key_mutations.create_api_key(info, name="Valid Name", expires_at=expires_at)

mock_api_key_create.assert_called_once_with(
user=user, name="Valid Name", expires_at=expires_at
)

assert isinstance(result, CreateApiKeyResult)
assert result.ok
assert result.code == "SUCCESS"
assert result.api_key == mock_instance
assert result.raw_key == "raw_key"

@patch("apps.api.internal.mutations.api_key.ApiKey.create", return_value=None)
def test_create_api_key_limit_reached(self, mock_api_key_create, api_key_mutations):
"""Test creating an API key when the user has reached their active key limit."""
Expand Down
26 changes: 23 additions & 3 deletions frontend/__tests__/unit/pages/ApiKeysPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ describe('ApiKeysPage Component', () => {
fireEvent.click(screen.getByRole('button', { name: /create api key/i }))

const expectedDate = format(addDays(new Date(), 30), 'yyyy-MM-dd')
const expectedIso = new Date(`${expectedDate}T00:00:00.000Z`).toISOString()
const expectedIso = new Date(`${expectedDate}T23:59:59.999Z`).toISOString()

Comment thread
Shashwat-Darshan marked this conversation as resolved.
await waitFor(() => {
expect(mockCreateMutation).toHaveBeenCalledWith({
Expand All @@ -176,7 +176,27 @@ describe('ApiKeysPage Component', () => {
expect(mockCreateMutation).toHaveBeenCalledWith({
variables: {
name: 'Custom Expiry Key',
expiresAt: new Date('2025-12-31T00:00:00.000Z').toISOString(),
expiresAt: new Date('2025-12-31T23:59:59.999Z').toISOString(),
},
})
})
})

test('creates API key for today using end-of-day UTC', async () => {
render(<ApiKeysPage />)
await openCreateModal()

const today = format(new Date(), 'yyyy-MM-dd')
fillKeyForm('Today Key', today)
fireEvent.click(screen.getByRole('button', { name: /create api key/i }))

const expectedIso = new Date(`${today}T23:59:59.999Z`).toISOString()

await waitFor(() => {
expect(mockCreateMutation).toHaveBeenCalledWith({
variables: {
name: 'Today Key',
expiresAt: expectedIso,
},
})
})
Expand Down Expand Up @@ -415,7 +435,7 @@ describe('ApiKeysPage Component', () => {
name: 'third key',
isRevoked: false,
createdAt: '2025-07-10T08:17:45.406011+00:00',
expiresAt: '2025-12-31T00:00:00+00:00',
expiresAt: '2025-12-31T23:59:59.999Z',
},
],
activeApiKeyCount: 3,
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/app/settings/api-keys/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import { ApiKeysSkeleton } from 'components/skeletons/ApiKeySkeleton'

const MAX_ACTIVE_KEYS = 3

const toEndOfDayUtcIso = (date: string): string =>
new Date(`${date}T23:59:59.999Z`).toISOString()
Comment thread
Shashwat-Darshan marked this conversation as resolved.
Outdated

Comment thread
Shashwat-Darshan marked this conversation as resolved.
Outdated
// Content state components
const ErrorState = () => (
<div className="rounded-md bg-red-50 p-4 text-red-700 dark:bg-red-900/20 dark:text-red-400">
Expand Down Expand Up @@ -184,7 +187,7 @@ export default function Page() {
}
const variables: { name: string; expiresAt: string } = {
name: newKeyName.trim(),
expiresAt: new Date(newKeyExpiry).toISOString(),
expiresAt: toEndOfDayUtcIso(newKeyExpiry),
}
createApiKey({ variables })
}
Expand Down