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
24 changes: 20 additions & 4 deletions src/services/auth/session-store.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@
* Writes to `data/config/sessions.json`; cleans up in beforeEach.
*/

import { describe, it, expect, beforeEach } from 'vitest'
import { readFile, unlink, writeFile } from 'node:fs/promises'
import { dataPath } from '@/core/paths.js'
import { describe, it, expect, beforeEach, beforeAll, afterAll } from 'vitest'
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import {
createSession,
validateAndTouch,
Expand All @@ -17,7 +18,22 @@ import {
_unlinkFile,
} from './session-store.js'

const SESSIONS_FILE = dataPath('config', 'sessions.json')
// Redirect the store at a private temp file (via the OPENALICE_SESSIONS_FILE
// seam) so this file never touches the real data/config/sessions.json and
// can't race with other specs (e.g. auth.spec.ts) under parallel runs.
let tmpDir: string
let SESSIONS_FILE: string

beforeAll(async () => {
tmpDir = await mkdtemp(join(tmpdir(), 'oa-session-store-'))
SESSIONS_FILE = join(tmpDir, 'sessions.json')
process.env['OPENALICE_SESSIONS_FILE'] = SESSIONS_FILE
})

afterAll(async () => {
delete process.env['OPENALICE_SESSIONS_FILE']
await rm(tmpDir, { recursive: true, force: true })
})

beforeEach(async () => {
await _reset()
Expand Down
8 changes: 7 additions & 1 deletion src/services/auth/session-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ import { dataPath } from '@/core/paths.js'
const SID_BYTES = 32
const DEFAULT_TTL_MS = 7 * 24 * 60 * 60 * 1000 // 7 days

const SESSIONS_FILE = () => dataPath('config', 'sessions.json')
// Resolved lazily on every call so the path can be redirected at runtime.
// `OPENALICE_SESSIONS_FILE` is a test-only seam (same spirit as the
// `_reset` / `_unlinkFile` exports below): specs point it at a private
// temp file so parallel test files don't race on — or clobber — the real
// `data/config/sessions.json`. Production never sets it.
const SESSIONS_FILE = () =>
process.env['OPENALICE_SESSIONS_FILE'] || dataPath('config', 'sessions.json')

export interface SessionRecord {
sid: string
Expand Down
21 changes: 20 additions & 1 deletion src/webui/middleware/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
* is fed via a mocked `c.env.incoming.socket.remoteAddress`.
*/

import { describe, it, expect, beforeEach } from 'vitest'
import { describe, it, expect, beforeEach, beforeAll, afterAll } from 'vitest'
import { mkdtemp, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { Hono } from 'hono'
import {
createAuthMiddleware,
Expand All @@ -20,6 +23,22 @@ import {
_reset,
} from '@/services/auth/session-store.js'

// These tests create real sessions through the store. Redirect it at a
// private temp file (OPENALICE_SESSIONS_FILE seam) so we neither clobber the
// operator's real data/config/sessions.json nor race session-store.spec.ts
// over the shared file under parallel runs.
let tmpDir: string

beforeAll(async () => {
tmpDir = await mkdtemp(join(tmpdir(), 'oa-auth-mw-'))
process.env['OPENALICE_SESSIONS_FILE'] = join(tmpDir, 'sessions.json')
})

afterAll(async () => {
delete process.env['OPENALICE_SESSIONS_FILE']
await rm(tmpDir, { recursive: true, force: true })
})

function makeApp(opts: Parameters<typeof createAuthMiddleware>[0]) {
const app = new Hono()
app.use('*', createAuthMiddleware(opts))
Expand Down
Loading