initial public release
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
package intake
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
// setupTestDB creates an in-memory SQLite DB with intake schema
|
||||
// Each call gets a fresh isolated database
|
||||
func setupTestDB(t *testing.T) *sql.DB {
|
||||
db, err := sql.Open("sqlite", "file::memory:")
|
||||
if err != nil {
|
||||
t.Fatalf("open in-memory db: %v", err)
|
||||
}
|
||||
|
||||
// Create tables
|
||||
schema := `
|
||||
CREATE TABLE IF NOT EXISTS intake_sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
current_phase INTEGER DEFAULT 0,
|
||||
is_complete BOOLEAN DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS intake_answers (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
session_id INTEGER NOT NULL,
|
||||
phase INTEGER NOT NULL,
|
||||
fields_json TEXT,
|
||||
nested_items_json TEXT,
|
||||
submitted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (session_id) REFERENCES intake_sessions(id),
|
||||
UNIQUE(session_id, phase)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_intake_answers_session_id ON intake_answers(session_id);
|
||||
`
|
||||
|
||||
if _, err := db.Exec(schema); err != nil {
|
||||
t.Fatalf("create schema: %v", err)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func TestNewSessionCreatesFirstSession(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
sess := NewSession(db)
|
||||
session, err := sess.LoadSession()
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSession: %v", err)
|
||||
}
|
||||
|
||||
if session.CurrentPhase != PhaseIdentity {
|
||||
t.Errorf("expected current phase PhaseIdentity (0), got %d", session.CurrentPhase)
|
||||
}
|
||||
if session.IsComplete {
|
||||
t.Errorf("expected IsComplete=false, got true")
|
||||
}
|
||||
if session.ID == 0 {
|
||||
t.Errorf("expected non-zero ID, got %d", session.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveAnswerPersistsAndLoads(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
sess := NewSession(db)
|
||||
session, err := sess.LoadSession()
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSession: %v", err)
|
||||
}
|
||||
|
||||
// Save an answer for Phase 0
|
||||
answer := FormAnswer{
|
||||
Phase: PhaseIdentity,
|
||||
Fields: map[string]string{
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"location": "San Francisco",
|
||||
"timezone": "PST",
|
||||
},
|
||||
SubmittedAt: time.Now(),
|
||||
}
|
||||
|
||||
if err := sess.SaveAnswer(session, answer); err != nil {
|
||||
t.Fatalf("SaveAnswer: %v", err)
|
||||
}
|
||||
|
||||
// Load session again
|
||||
session2, err := sess.LoadSession()
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSession (2nd): %v", err)
|
||||
}
|
||||
|
||||
if len(session2.Answers) != 1 {
|
||||
t.Errorf("expected 1 answer, got %d", len(session2.Answers))
|
||||
}
|
||||
|
||||
loaded := session2.Answers[0]
|
||||
if loaded.Phase != PhaseIdentity {
|
||||
t.Errorf("expected phase PhaseIdentity, got %d", loaded.Phase)
|
||||
}
|
||||
if loaded.Fields["name"] != "John Doe" {
|
||||
t.Errorf("expected name 'John Doe', got %q", loaded.Fields["name"])
|
||||
}
|
||||
if loaded.Fields["email"] != "john@example.com" {
|
||||
t.Errorf("expected email 'john@example.com', got %q", loaded.Fields["email"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestNextPhaseAdvancesCorrectly(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
sess := NewSession(db)
|
||||
session, _ := sess.LoadSession()
|
||||
|
||||
if session.CurrentPhase != PhaseIdentity {
|
||||
t.Fatalf("expected current phase 0, got %d", session.CurrentPhase)
|
||||
}
|
||||
|
||||
// Advance to Phase 1
|
||||
next := sess.NextPhase(session)
|
||||
if next != PhaseEducation {
|
||||
t.Errorf("expected PhaseEducation (1), got %d", next)
|
||||
}
|
||||
|
||||
// Set current phase to Voice (7)
|
||||
session.CurrentPhase = PhaseVoice
|
||||
next = sess.NextPhase(session)
|
||||
if next != PhaseComplete {
|
||||
t.Errorf("expected PhaseComplete (8), got %d", next)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanAdvanceIdentityPhase(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
sess := NewSession(db)
|
||||
session, _ := sess.LoadSession()
|
||||
|
||||
// Initially cannot advance
|
||||
if sess.CanAdvance(session) {
|
||||
t.Errorf("expected CanAdvance=false without answers, got true")
|
||||
}
|
||||
|
||||
// Add incomplete answer
|
||||
answer := FormAnswer{
|
||||
Phase: PhaseIdentity,
|
||||
Fields: map[string]string{
|
||||
"name": "John Doe",
|
||||
// Missing email, location, timezone
|
||||
},
|
||||
SubmittedAt: time.Now(),
|
||||
}
|
||||
if err := sess.SaveAnswer(session, answer); err != nil {
|
||||
t.Fatalf("SaveAnswer failed: %v", err)
|
||||
}
|
||||
|
||||
// Reload and check
|
||||
session, _ = sess.LoadSession()
|
||||
if sess.CanAdvance(session) {
|
||||
t.Errorf("expected CanAdvance=false with incomplete identity, got true")
|
||||
}
|
||||
|
||||
// For testing complete identity, create a new session with complete answer
|
||||
// (can't update same phase due to UNIQUE constraint)
|
||||
db2 := setupTestDB(t)
|
||||
defer db2.Close()
|
||||
|
||||
sess2 := NewSession(db2)
|
||||
session2, _ := sess2.LoadSession()
|
||||
|
||||
completedAnswer := FormAnswer{
|
||||
Phase: PhaseIdentity,
|
||||
Fields: map[string]string{
|
||||
"name": "John Doe",
|
||||
"email": "john@example.com",
|
||||
"location": "SF",
|
||||
"timezone": "PST",
|
||||
},
|
||||
SubmittedAt: time.Now(),
|
||||
}
|
||||
err := sess2.SaveAnswer(session2, completedAnswer)
|
||||
if err != nil {
|
||||
t.Fatalf("SaveAnswer failed: %v", err)
|
||||
}
|
||||
|
||||
session2, err = sess2.LoadSession()
|
||||
if err != nil {
|
||||
t.Fatalf("LoadSession failed: %v", err)
|
||||
}
|
||||
if !sess2.CanAdvance(session2) {
|
||||
t.Errorf("expected CanAdvance=true with complete identity, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanAdvanceMultiItemPhases(t *testing.T) {
|
||||
// Test Education phase without items
|
||||
db1 := setupTestDB(t)
|
||||
defer db1.Close()
|
||||
|
||||
sess1 := NewSession(db1)
|
||||
session1, _ := sess1.LoadSession()
|
||||
session1.CurrentPhase = PhaseEducation
|
||||
|
||||
// Answer without nested items
|
||||
answer1 := FormAnswer{
|
||||
Phase: PhaseEducation,
|
||||
Fields: map[string]string{"dummy": "value"},
|
||||
NestedItems: []map[string]string{},
|
||||
SubmittedAt: time.Now(),
|
||||
}
|
||||
sess1.SaveAnswer(session1, answer1)
|
||||
|
||||
session1, _ = sess1.LoadSession()
|
||||
if sess1.CanAdvance(session1) {
|
||||
t.Errorf("expected CanAdvance=false for Education with no items, got true")
|
||||
}
|
||||
|
||||
// Test Education phase with items (new session)
|
||||
db2 := setupTestDB(t)
|
||||
defer db2.Close()
|
||||
|
||||
sess2 := NewSession(db2)
|
||||
session2, _ := sess2.LoadSession()
|
||||
session2.CurrentPhase = PhaseEducation
|
||||
|
||||
answer2 := FormAnswer{
|
||||
Phase: PhaseEducation,
|
||||
Fields: map[string]string{"dummy": "value"},
|
||||
NestedItems: []map[string]string{
|
||||
{"institution": "MIT", "degree": "BS"},
|
||||
},
|
||||
SubmittedAt: time.Now(),
|
||||
}
|
||||
sess2.SaveAnswer(session2, answer2)
|
||||
|
||||
session2, _ = sess2.LoadSession()
|
||||
if !sess2.CanAdvance(session2) {
|
||||
t.Errorf("expected CanAdvance=true for Education with items, got false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarkComplete(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
sess := NewSession(db)
|
||||
session, _ := sess.LoadSession()
|
||||
|
||||
if session.IsComplete {
|
||||
t.Fatalf("expected IsComplete=false initially")
|
||||
}
|
||||
|
||||
if err := sess.MarkComplete(session); err != nil {
|
||||
t.Fatalf("MarkComplete: %v", err)
|
||||
}
|
||||
|
||||
session, _ = sess.LoadSession()
|
||||
if !session.IsComplete {
|
||||
t.Errorf("expected IsComplete=true after MarkComplete, got false")
|
||||
}
|
||||
if session.CurrentPhase != PhaseComplete {
|
||||
t.Errorf("expected CurrentPhase=PhaseComplete, got %d", session.CurrentPhase)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user