Files
supabase/apps/studio/tests/components/AuditLogs/AuditLogs.utils.test.ts
Samir Ketema ee5d4a9314 chore: remove format param from audit log query (#45466)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Cleanup after shipping https://github.com/supabase/supabase/pull/45389,
the backend is now defaulting to the new v2 `format`, and made `format`
param optional.

So this:
- removes references to `v2` naming, as this is the only format
- removes the `format` query param from the audit logs API calls

## What is the current behavior?

Same audit log functionality shown in
https://github.com/supabase/supabase/pull/45389

## What is the new behavior?

Functionally the same behavior for audit logs.

- [x] Manual test in staging

## Additional context

⚠️ Will leave the `do-not-merge` tag on until:
- [ ] backend `format` optional PR lands in production.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Consolidated audit log type definitions and updated internal API
request formatting for audit endpoints across Account and Organization
audit log components. No changes to user-facing functionality or audit
log display.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-05-04 16:24:44 -07:00

133 lines
4.2 KiB
TypeScript

import dayjs from 'dayjs'
import { describe, expect, test } from 'vitest'
import {
filterByProjects,
filterByUsers,
sortAuditLogs,
} from '@/components/interfaces/Organization/AuditLogs/AuditLogs.utils'
import {
TIMESTAMP_MICROS_PER_MS,
type AuditLog,
} from '@/data/organizations/organization-audit-logs-query'
// Timestamps are in microseconds (e.g. 1777471903844000 = April 2026)
const TS_A = 1777471903844000
const TS_B = 1777471903845000
const TS_C = 1777471903846000
function makeLog(overrides: Partial<AuditLog> = {}): AuditLog {
return {
timestamp: TS_A,
request_id: 'req-1',
action: { name: 'test', method: 'GET', route: '/api/test', status: 200 },
actor: { token_type: 'bearer' },
...overrides,
}
}
const logA = makeLog({ timestamp: TS_A, request_id: 'req-a', project_ref: 'proj-1' })
const logB = makeLog({
timestamp: TS_B,
request_id: 'req-b',
project_ref: 'proj-2',
actor: { token_type: 'bearer', user_id: 'user-1' },
})
const logC = makeLog({
timestamp: TS_C,
request_id: 'req-c',
actor: { token_type: 'bearer', user_id: 'user-2' },
})
describe('timestamp conversion (microseconds → milliseconds)', () => {
test('dividing by 1000 produces a valid date', () => {
expect(dayjs(TS_A / TIMESTAMP_MICROS_PER_MS).isValid()).toBe(true)
})
test('produces the correct year', () => {
expect(dayjs(TS_A / TIMESTAMP_MICROS_PER_MS).year()).toBe(2026)
})
test('dayjs.unix on a microsecond value produces an invalid date', () => {
// Guard: confirms the bug we fixed — dayjs.unix() treats the value as seconds,
// overflowing JS Date's max and producing "Invalid Date"
expect(dayjs.unix(TS_A).isValid()).toBe(false)
})
})
describe('sortAuditLogs', () => {
test('sorts descending (newest first)', () => {
const result = sortAuditLogs([logA, logC, logB], true)
expect(result.map((l) => l.request_id)).toEqual(['req-c', 'req-b', 'req-a'])
})
test('sorts ascending (oldest first)', () => {
const result = sortAuditLogs([logC, logA, logB], false)
expect(result.map((l) => l.request_id)).toEqual(['req-a', 'req-b', 'req-c'])
})
test('does not mutate the input array', () => {
const input = [logC, logA]
sortAuditLogs(input, true)
expect(input[0].request_id).toBe('req-c')
})
test('returns empty array unchanged', () => {
expect(sortAuditLogs([], true)).toEqual([])
})
test('handles a single log', () => {
expect(sortAuditLogs([logA], true)).toEqual([logA])
})
})
describe('filterByUsers', () => {
test('returns all logs when filter list is empty', () => {
expect(filterByUsers([logA, logB, logC], [])).toEqual([logA, logB, logC])
})
test('filters to matching user_id', () => {
const result = filterByUsers([logA, logB, logC], ['user-1'])
expect(result.map((l) => l.request_id)).toEqual(['req-b'])
})
test('filters to multiple user_ids', () => {
const result = filterByUsers([logA, logB, logC], ['user-1', 'user-2'])
expect(result.map((l) => l.request_id)).toEqual(['req-b', 'req-c'])
})
test('excludes logs with no user_id when filtering', () => {
const result = filterByUsers([logA, logB], ['user-1'])
expect(result.map((l) => l.request_id)).toEqual(['req-b'])
})
test('returns empty array when no logs match', () => {
expect(filterByUsers([logA, logB, logC], ['user-unknown'])).toEqual([])
})
})
describe('filterByProjects', () => {
test('returns all logs when filter list is empty', () => {
expect(filterByProjects([logA, logB, logC], [])).toEqual([logA, logB, logC])
})
test('filters to matching project_ref', () => {
const result = filterByProjects([logA, logB, logC], ['proj-1'])
expect(result.map((l) => l.request_id)).toEqual(['req-a'])
})
test('filters to multiple project_refs', () => {
const result = filterByProjects([logA, logB, logC], ['proj-1', 'proj-2'])
expect(result.map((l) => l.request_id)).toEqual(['req-a', 'req-b'])
})
test('excludes logs with no project_ref when filtering', () => {
const result = filterByProjects([logA, logC], ['proj-1'])
expect(result.map((l) => l.request_id)).toEqual(['req-a'])
})
test('returns empty array when no logs match', () => {
expect(filterByProjects([logA, logB, logC], ['proj-unknown'])).toEqual([])
})
})