Files
morsequest/server/tests/db.test.js
T
kitadmin 9b31119c7a feat: database layer with better-sqlite3, full schema and query functions
Implements Task 4 (TDD): wrote failing tests first, then the full SQLite
database layer covering profiles, magic tokens, sessions, user progress,
mnemonics, and letter stats. All 14 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 00:34:59 +00:00

143 lines
3.7 KiB
JavaScript

'use strict'
const { test, describe, before, after } = require('node:test')
const assert = require('node:assert/strict')
const { initDb } = require('../db.js')
let db
before(() => {
db = initDb(':memory:')
})
after(() => {
db._db.close()
})
describe('profiles', () => {
test('createProfile creates a profile', () => {
const p = db.createProfile('test@example.com', 'Tester')
assert.equal(p.email, 'test@example.com')
assert.equal(p.display_name, 'Tester')
assert.ok(p.id)
})
test('findProfileByEmail returns the profile', () => {
const p = db.findProfileByEmail('test@example.com')
assert.equal(p.email, 'test@example.com')
})
test('findProfileByEmail returns null for unknown email', () => {
assert.equal(db.findProfileByEmail('nobody@example.com'), null)
})
})
describe('magic tokens', () => {
let profileId
before(() => {
profileId = db.createProfile('magic@example.com', 'Magic').id
})
test('createMagicToken and useMagicToken returns profileId', () => {
const expires = Date.now() + 86400000
db.createMagicToken('tok123', profileId, expires)
const result = db.useMagicToken('tok123')
assert.equal(result, profileId)
})
test('useMagicToken returns null for used token', () => {
assert.equal(db.useMagicToken('tok123'), null)
})
test('useMagicToken returns null for expired token', () => {
db.createMagicToken('expiredtok', profileId, Date.now() - 1000)
assert.equal(db.useMagicToken('expiredtok'), null)
})
})
describe('sessions', () => {
let profileId
before(() => {
profileId = db.createProfile('session@example.com', 'Session').id
})
test('createSession and getSession returns profile_id', () => {
db.createSession('sess1', profileId)
const s = db.getSession('sess1')
assert.equal(s.profile_id, profileId)
})
test('getSession returns null for unknown token', () => {
assert.equal(db.getSession('unknown'), null)
})
test('deleteSession removes session', () => {
db.deleteSession('sess1')
assert.equal(db.getSession('sess1'), null)
})
})
describe('progress', () => {
let profileId
before(() => {
profileId = db.createProfile('progress@example.com', 'Progress').id
})
test('getOrCreateProgress returns default progress', () => {
const p = db.getOrCreateProgress(profileId)
assert.equal(p.level, 1)
assert.equal(p.score, 0)
assert.equal(p.streak, 0)
})
test('recordAnswer increments score and streak on correct', () => {
db.recordAnswer(profileId, 'A', true)
const p = db.getOrCreateProgress(profileId)
assert.ok(p.score > 0)
assert.equal(p.streak, 1)
assert.equal(p.total_correct, 1)
assert.equal(p.total_attempts, 1)
})
test('recordAnswer resets streak on incorrect', () => {
db.recordAnswer(profileId, 'A', false)
const p = db.getOrCreateProgress(profileId)
assert.equal(p.streak, 0)
assert.equal(p.total_attempts, 2)
})
})
describe('mnemonics', () => {
let profileId
before(() => {
profileId = db.createProfile('mnemonic@example.com', 'Mnemonic').id
})
test('saveMnemonic and getMnemonics returns map', () => {
db.saveMnemonic(profileId, 'A', 'go WALKING')
db.saveMnemonic(profileId, 'E', 'go')
const m = db.getMnemonics(profileId)
assert.equal(m.A, 'go WALKING')
assert.equal(m.E, 'go')
})
})
describe('letter stats', () => {
let profileId
before(() => {
profileId = db.createProfile('stats@example.com', 'Stats').id
})
test('recordAnswer tracks letter stats', () => {
db.recordAnswer(profileId, 'S', true)
db.recordAnswer(profileId, 'S', false)
const stats = db.getLetterStats(profileId)
assert.equal(stats.S.correct, 1)
assert.equal(stats.S.attempts, 2)
})
})