Documentation
¶
Index ¶
Constants ¶
const BaseTimestamp = "2024-01-15 10:00:00"
BaseTimestamp is the fixed timestamp used for all created_at fields. Using a fixed timestamp ensures deterministic test data.
Variables ¶
var ( // User 1: Bob Johnson User1Name = "Bob Johnson" User1Email = "user1@example.com" User1Age = 19 User1Active = true User1Bio = "Software engineer" User1FirstName = "Bob" User1LastName = "Johnson" // User 5: Frank Garcia (no bio) User5Name = "Frank Garcia" User5Email = "user5@example.com" User5Age = 23 User5Active = true // User 10: Kate Anderson (inactive) User10Name = "Kate Anderson" User10Email = "user10@example.com" User10Age = 28 User10Active = false // Product 1 Product1Name = "Gadget 1" Product1Price = "11.50" Product1InStock = true Product1Category = "electronics" // Product 7 (out of stock) Product7Name = "Gizmo 7" Product7Price = "17.50" Product7InStock = false Product7Category = "health" // Category: electronics CategoryElectronicsName = "Electronics" CategoryElectronicsDescription = "Gadgets, devices, and electronic accessories" CategoryElectronicsOrder = 1 // All category slugs (sorted) AllCategorySlugs = []string{ "automotive", "books", "clothing", "electronics", "food", "health", "home", "office", "outdoor", "toys", } )
Known test data values (for assertions)
var DDLTestCases = []CommandTestCase{ { Name: "DDL_CreateDirExists", Category: "ddl", Input: CommandInput{Op: "ls", Path: ".create"}, Expected: ExpectedOutput{ Contains: []string{}, }, }, { Name: "DDL_MkdirCreateTable", Category: "ddl", Input: CommandInput{Op: "mkdir", Path: ".create/ddl_test_table"}, Expected: ExpectedOutput{}, }, { Name: "DDL_CreateHasControlFiles", Category: "ddl", Input: CommandInput{Op: "ls", Path: ".create/ddl_test_table"}, Expected: ExpectedOutput{ Contains: []string{"sql", ".test", ".commit", ".abort"}, }, }, { Name: "DDL_ReadSqlTemplate", Category: "ddl", Input: CommandInput{Op: "cat", Path: ".create/ddl_test_table/sql"}, Expected: ExpectedOutput{ Contains: []string{"CREATE TABLE", "ddl_test_table"}, }, }, { Name: "DDL_WriteSql", Category: "ddl", Input: CommandInput{Op: "echo", Path: ".create/ddl_test_table/sql", Content: "CREATE TABLE ddl_test_table (id SERIAL PRIMARY KEY, name TEXT);"}, Expected: ExpectedOutput{}, }, { Name: "DDL_TestValidation", Category: "ddl", Input: CommandInput{Op: "touch", Path: ".create/ddl_test_table/.test"}, Expected: ExpectedOutput{}, }, { Name: "DDL_TestValidationResult", Category: "ddl", Input: CommandInput{Op: "cat", Path: ".create/ddl_test_table/test.log"}, Expected: ExpectedOutput{ Contains: []string{"OK"}, }, }, { Name: "DDL_Commit", Category: "ddl", Input: CommandInput{Op: "touch", Path: ".create/ddl_test_table/.commit"}, Expected: ExpectedOutput{}, }, { Name: "DDL_VerifyTableCreated", Category: "ddl", Input: CommandInput{Op: "ls", Path: "ddl_test_table"}, Expected: ExpectedOutput{ Contains: []string{".info"}, }, }, { Name: "DDL_ModifyHasControlFiles", Category: "ddl", Input: CommandInput{Op: "ls", Path: "ddl_test_table/.modify"}, Expected: ExpectedOutput{ Contains: []string{"sql", ".test", ".commit", ".abort"}, }, }, { Name: "DDL_DeleteHasControlFiles", Category: "ddl", Input: CommandInput{Op: "ls", Path: "ddl_test_table/.delete"}, Expected: ExpectedOutput{ Contains: []string{"sql", ".test", ".commit", ".abort"}, }, }, { Name: "DDL_AbortDelete", Category: "ddl", Input: CommandInput{Op: "touch", Path: "ddl_test_table/.delete/.abort"}, Expected: ExpectedOutput{}, }, { Name: "DDL_DeleteSqlAfterAbort", Category: "ddl", Input: CommandInput{Op: "cat", Path: "ddl_test_table/.delete/sql"}, Expected: ExpectedOutput{ Contains: []string{"DROP TABLE"}, }, }, { Name: "DDL_WriteDeleteSql", Category: "ddl", Input: CommandInput{Op: "echo", Path: "ddl_test_table/.delete/sql", Content: "DROP TABLE ddl_test_table;"}, Expected: ExpectedOutput{}, }, { Name: "DDL_DeleteTestValidation", Category: "ddl", Input: CommandInput{Op: "touch", Path: "ddl_test_table/.delete/.test"}, Expected: ExpectedOutput{}, }, { Name: "DDL_DeleteTestResult", Category: "ddl", Input: CommandInput{Op: "cat", Path: "ddl_test_table/.delete/test.log"}, Expected: ExpectedOutput{ Contains: []string{"OK"}, }, }, { Name: "DDL_DeleteTable", Category: "ddl", Input: CommandInput{Op: "touch", Path: "ddl_test_table/.delete/.commit"}, Expected: ExpectedOutput{}, }, { Name: "DDL_VerifyTableDeleted", Category: "ddl", Input: CommandInput{Op: "ls", Path: ""}, Expected: ExpectedOutput{ NotContains: []string{"ddl_test_table"}, }, }, }
DDLTestCases contains DDL test cases (create/alter/drop tables/indexes).
var ReadTestCases = []CommandTestCase{ { Name: "ListTables", Category: "read/ls", Input: CommandInput{Op: "ls", Path: ""}, Expected: ExpectedOutput{ Contains: []string{"users", "products", "categories", "orders"}, }, }, { Name: "ListCategories_TextPK", Category: "read/ls", Input: CommandInput{Op: "ls", Path: "categories"}, Expected: ExpectedOutput{ Contains: []string{"electronics", "home", "clothing", "books", "toys"}, }, }, { Name: "ListUserRow", Category: "read/ls", Input: CommandInput{Op: "ls", Path: "users/1"}, Expected: ExpectedOutput{ Contains: []string{"id", "name", "email", "age", "active", "bio", "first_name", "last_name"}, }, }, { Name: "ListProductRow", Category: "read/ls", Input: CommandInput{Op: "ls", Path: "products/1"}, Expected: ExpectedOutput{ Contains: []string{"id", "name", "price", "in_stock", "category"}, }, }, { Name: "InfoCount_Users", Category: "read/info", Input: CommandInput{Op: "cat", Path: "users/.info/count"}, Expected: ExpectedOutput{Exact: "100"}, }, { Name: "InfoCount_Products", Category: "read/info", Input: CommandInput{Op: "cat", Path: "products/.info/count"}, Expected: ExpectedOutput{Exact: "20"}, }, { Name: "InfoCount_Categories", Category: "read/info", Input: CommandInput{Op: "cat", Path: "categories/.info/count"}, Expected: ExpectedOutput{Exact: "10"}, }, { Name: "InfoCount_Orders", Category: "read/info", Input: CommandInput{Op: "cat", Path: "orders/.info/count"}, Expected: ExpectedOutput{Exact: "200"}, }, { Name: "InfoColumns_Users", Category: "read/info", Input: CommandInput{Op: "cat", Path: "users/.info/columns"}, Expected: ExpectedOutput{ Contains: []string{"id", "name", "email", "age", "active", "bio", "first_name", "last_name", "created_at"}, }, }, { Name: "InfoColumns_Categories", Category: "read/info", Input: CommandInput{Op: "cat", Path: "categories/.info/columns"}, Expected: ExpectedOutput{ Contains: []string{"slug", "name", "description", "icon", "display_order", "active", "created_at"}, }, }, { Name: "ReadColumn_UserName", Category: "read/column", Input: CommandInput{Op: "cat", Path: "users/1/name"}, Expected: ExpectedOutput{Exact: User1Name}, }, { Name: "ReadColumn_UserEmail", Category: "read/column", Input: CommandInput{Op: "cat", Path: "users/1/email"}, Expected: ExpectedOutput{Exact: User1Email}, }, { Name: "ReadColumn_UserAge", Category: "read/column", Input: CommandInput{Op: "cat", Path: "users/1/age"}, Expected: ExpectedOutput{Exact: "19"}, }, { Name: "ReadColumn_UserActive", Category: "read/column", Input: CommandInput{Op: "cat", Path: "users/1/active"}, Expected: ExpectedOutput{Exact: "t"}, }, { Name: "ReadColumn_UserBio", Category: "read/column", Input: CommandInput{Op: "cat", Path: "users/1/bio"}, Expected: ExpectedOutput{Exact: User1Bio}, }, { Name: "ReadColumn_UserBio_Null", Category: "read/column", Input: CommandInput{Op: "cat", Path: "users/5/bio"}, Expected: ExpectedOutput{Exact: ""}, }, { Name: "ReadColumn_UserInactive", Category: "read/column", Input: CommandInput{Op: "cat", Path: "users/10/active"}, Expected: ExpectedOutput{Exact: "f"}, }, { Name: "ReadColumn_ProductName", Category: "read/column", Input: CommandInput{Op: "cat", Path: "products/1/name"}, Expected: ExpectedOutput{Exact: Product1Name}, }, { Name: "ReadColumn_ProductPrice", Category: "read/column", Input: CommandInput{Op: "cat", Path: "products/1/price"}, Expected: ExpectedOutput{Exact: Product1Price}, }, { Name: "ReadColumn_ProductInStock", Category: "read/column", Input: CommandInput{Op: "cat", Path: "products/1/in_stock"}, Expected: ExpectedOutput{Exact: "t"}, }, { Name: "ReadColumn_ProductOutOfStock", Category: "read/column", Input: CommandInput{Op: "cat", Path: "products/7/in_stock"}, Expected: ExpectedOutput{Exact: "f"}, }, { Name: "ReadColumn_ProductCategory", Category: "read/column", Input: CommandInput{Op: "cat", Path: "products/1/category"}, Expected: ExpectedOutput{Exact: Product1Category}, }, { Name: "ReadColumn_CategoryName_TextPK", Category: "read/column", Input: CommandInput{Op: "cat", Path: "categories/electronics/name"}, Expected: ExpectedOutput{Exact: CategoryElectronicsName}, }, { Name: "ReadColumn_CategoryDescription", Category: "read/column", Input: CommandInput{Op: "cat", Path: "categories/electronics/description"}, Expected: ExpectedOutput{Exact: CategoryElectronicsDescription}, }, { Name: "ReadColumn_CategoryDisplayOrder", Category: "read/column", Input: CommandInput{Op: "cat", Path: "categories/electronics/display_order"}, Expected: ExpectedOutput{Exact: "1"}, }, { Name: "ReadRowJSON_User1", Category: "read/row", Input: CommandInput{Op: "cat", Path: "users/1/.json"}, Expected: ExpectedOutput{ JSONFields: map[string]any{ "id": float64(1), "name": User1Name, "email": User1Email, "age": float64(User1Age), "active": User1Active, "bio": User1Bio, "first_name": User1FirstName, "last_name": User1LastName, }, }, }, { Name: "ReadRowJSON_User5_NullBio", Category: "read/row", Input: CommandInput{Op: "cat", Path: "users/5/.json"}, Expected: ExpectedOutput{ JSONFields: map[string]any{ "id": float64(5), "name": User5Name, "bio": nil, }, }, }, { Name: "ReadRowJSON_Category_TextPK", Category: "read/row", Input: CommandInput{Op: "cat", Path: "categories/electronics/.json"}, Expected: ExpectedOutput{ JSONFields: map[string]any{ "slug": "electronics", "name": CategoryElectronicsName, "description": CategoryElectronicsDescription, "display_order": float64(CategoryElectronicsOrder), }, }, }, { Name: "ReadRowTSV_User1", Category: "read/row", Input: CommandInput{Op: "cat", Path: "users/1/.tsv"}, Expected: ExpectedOutput{ Contains: []string{"1", User1Name, User1Email, "19", "\tt\t"}, }, }, { Name: "ReadRowCSV_User1", Category: "read/row", Input: CommandInput{Op: "cat", Path: "users/1/.csv"}, Expected: ExpectedOutput{ Contains: []string{"1", User1Name, User1Email}, }, }, { Name: "FilterActiveUsers", Category: "read/filter", Input: CommandInput{Op: "ls", Path: "users/.filter/active/true"}, Expected: ExpectedOutput{ Contains: []string{"1", "2", "3", "4", "5"}, }, }, { Name: "FilterInactiveUsers", Category: "read/filter", Input: CommandInput{Op: "ls", Path: "users/.filter/active/false"}, Expected: ExpectedOutput{ Contains: []string{"10", "20", "30"}, }, }, { Name: "FilterProductsInStock", Category: "read/filter", Input: CommandInput{Op: "ls", Path: "products/.filter/in_stock/true"}, Expected: ExpectedOutput{ Contains: []string{"1", "2", "3"}, }, }, { Name: "FilterProductsOutOfStock", Category: "read/filter", Input: CommandInput{Op: "ls", Path: "products/.filter/in_stock/false"}, Expected: ExpectedOutput{ Contains: []string{"7", "14"}, }, }, { Name: "FilterThenReadColumn", Category: "read/filter", Input: CommandInput{Op: "cat", Path: "users/.filter/active/true/1/name"}, Expected: ExpectedOutput{Exact: User1Name}, }, { Name: "FirstFiveUsers", Category: "read/pagination", Input: CommandInput{Op: "ls", Path: "users/.first/5"}, Expected: ExpectedOutput{ Contains: []string{"1", "2", "3", "4", "5"}, }, }, { Name: "LastFiveUsers", Category: "read/pagination", Input: CommandInput{Op: "ls", Path: "users/.last/5"}, Expected: ExpectedOutput{ Contains: []string{"96", "97", "98", "99", "100"}, }, }, { Name: "FirstThreeProducts", Category: "read/pagination", Input: CommandInput{Op: "ls", Path: "products/.first/3"}, Expected: ExpectedOutput{ Contains: []string{"1", "2", "3"}, }, }, { Name: "LastThreeProducts", Category: "read/pagination", Input: CommandInput{Op: "ls", Path: "products/.last/3"}, Expected: ExpectedOutput{ Contains: []string{"18", "19", "20"}, }, }, { Name: "FirstThenReadColumn", Category: "read/pagination", Input: CommandInput{Op: "cat", Path: "users/.first/1/1/name"}, Expected: ExpectedOutput{Exact: User1Name}, }, { Name: "OrderByAge_First5", Category: "read/order", Input: CommandInput{Op: "ls", Path: "users/.order/age/.first/5"}, Expected: ExpectedOutput{ Contains: []string{".export", ".info"}, }, }, { Name: "OrderByPrice_First3", Category: "read/order", Input: CommandInput{Op: "ls", Path: "products/.order/price/.first/3"}, Expected: ExpectedOutput{ Contains: []string{"1", "2", "3"}, }, }, { Name: "ExportCategoriesJSON", Category: "read/export", Input: CommandInput{Op: "cat", Path: "categories/.export/json"}, Expected: ExpectedOutput{ JSONArray: true, JSONLength: 10, }, }, { Name: "ExportCategoriesCSV", Category: "read/export", Input: CommandInput{Op: "cat", Path: "categories/.export/csv"}, Expected: ExpectedOutput{ LineCount: 10, Contains: []string{"electronics", "Electronics"}, }, }, { Name: "ExportCategoriesCSVHeaders", Category: "read/export", Input: CommandInput{Op: "cat", Path: "categories/.export/.with-headers/csv"}, Expected: ExpectedOutput{ LineCount: 11, Contains: []string{"slug", "name", "description"}, }, }, { Name: "ExportFirstFiveUsersJSON", Category: "read/export", Input: CommandInput{Op: "cat", Path: "users/.first/5/.export/json"}, Expected: ExpectedOutput{ JSONArray: true, JSONLength: 5, }, }, { Name: "ExportFilteredProductsJSON", Category: "read/export", Input: CommandInput{Op: "cat", Path: "products/.filter/in_stock/false/.export/json"}, Expected: ExpectedOutput{ JSONArray: true, }, }, { Name: "ExportAllProductsJSON", Category: "read/export", Input: CommandInput{Op: "cat", Path: "products/.export/json"}, Expected: ExpectedOutput{ JSONArray: true, }, }, { Name: "ExportInStockProductsJSON", Category: "read/export", Input: CommandInput{Op: "cat", Path: "products/.filter/in_stock/true/.export/json"}, Expected: ExpectedOutput{ JSONArray: true, }, }, { Name: "ExportAllProductsJSON_AfterFiltered", Category: "read/export", Input: CommandInput{Op: "cat", Path: "products/.export/json"}, Expected: ExpectedOutput{ JSONArray: true, }, }, { Name: "FilterThenFirst", Category: "read/pipeline", Input: CommandInput{Op: "ls", Path: "users/.filter/active/true/.first/10"}, Expected: ExpectedOutput{ Contains: []string{"1", "2", "3"}, }, }, { Name: "FilterThenOrderThenFirst", Category: "read/pipeline", Input: CommandInput{Op: "ls", Path: "users/.filter/active/true/.order/age/.first/5"}, Expected: ExpectedOutput{ Contains: []string{".export", ".info"}, }, }, { Name: "FilterThenExport", Category: "read/pipeline", Input: CommandInput{Op: "cat", Path: "products/.filter/in_stock/true/.first/5/.export/json"}, Expected: ExpectedOutput{ JSONArray: true, JSONLength: 5, }, }, { Name: "ListActiveUsersView", Category: "read/view", Input: CommandInput{Op: "ls", Path: "active_users"}, Expected: ExpectedOutput{ Contains: []string{"1", "2", "3"}, }, Skip: "Views not properly exposed via NFS yet", }, { Name: "ActiveUsersViewCount", Category: "read/view", Input: CommandInput{Op: "cat", Path: "active_users/.info/count"}, Expected: ExpectedOutput{Exact: "90"}, Skip: "Views not properly exposed via NFS yet", }, { Name: "ReadActiveUserFromView", Category: "read/view", Input: CommandInput{Op: "cat", Path: "active_users/1/name"}, Expected: ExpectedOutput{Exact: User1Name}, Skip: "Views not properly exposed via NFS yet", }, { Name: "OrderSummaryViewColumns", Category: "read/view", Input: CommandInput{Op: "cat", Path: "order_summary/.info/columns"}, Expected: ExpectedOutput{ Contains: []string{"order_id", "user_name", "product_name", "category_name"}, }, Skip: "Views don't have primary keys - needs separate fix", }, { Name: "ReadNonExistentUser", Category: "read/error", Input: CommandInput{Op: "cat", Path: "users/9999/name"}, Expected: ExpectedOutput{ Error: true, }, }, { Name: "ReadNonExistentColumn", Category: "read/error", Input: CommandInput{Op: "cat", Path: "users/1/nonexistent"}, Expected: ExpectedOutput{ Error: true, }, }, { Name: "ReadNonExistentTable", Category: "read/error", Input: CommandInput{Op: "ls", Path: "nonexistent_table"}, Expected: ExpectedOutput{ Error: true, }, }, }
ReadTestCases contains all read-only test cases. These run first and don't modify data.
var WriteTestCases = []CommandTestCase{ { Name: "WriteColumn_UpdateName", Category: "write/column", Input: CommandInput{Op: "echo", Path: "users/100/name", Content: "Updated Name"}, Expected: ExpectedOutput{Error: false}, }, { Name: "WriteColumn_VerifyName", Category: "write/column", Input: CommandInput{Op: "cat", Path: "users/100/name"}, Expected: ExpectedOutput{Exact: "Updated Name"}, }, { Name: "WriteColumn_UpdateEmail", Category: "write/column", Input: CommandInput{Op: "echo", Path: "users/100/email", Content: "updated@example.com"}, Expected: ExpectedOutput{Error: false}, }, { Name: "WriteColumn_VerifyEmail", Category: "write/column", Input: CommandInput{Op: "cat", Path: "users/100/email"}, Expected: ExpectedOutput{Exact: "updated@example.com"}, }, { Name: "WriteColumn_UpdateAge", Category: "write/column", Input: CommandInput{Op: "echo", Path: "users/100/age", Content: "99"}, Expected: ExpectedOutput{Error: false}, }, { Name: "WriteColumn_VerifyAge", Category: "write/column", Input: CommandInput{Op: "cat", Path: "users/100/age"}, Expected: ExpectedOutput{Exact: "99"}, }, { Name: "WriteColumn_UpdateBoolean", Category: "write/column", Input: CommandInput{Op: "echo", Path: "users/100/active", Content: "false"}, Expected: ExpectedOutput{Error: false}, }, { Name: "WriteColumn_VerifyBoolean", Category: "write/column", Input: CommandInput{Op: "cat", Path: "users/100/active"}, Expected: ExpectedOutput{Exact: "f"}, }, { Name: "WriteColumn_SetNull", Category: "write/column", Input: CommandInput{Op: "echo", Path: "users/100/bio", Content: ""}, Expected: ExpectedOutput{Error: false}, }, { Name: "WriteColumn_VerifyNull", Category: "write/column", Input: CommandInput{Op: "cat", Path: "users/100/bio"}, Expected: ExpectedOutput{Exact: ""}, }, { Name: "WriteColumn_ProductPrice", Category: "write/column", Input: CommandInput{Op: "echo", Path: "products/20/price", Content: "99.99"}, Expected: ExpectedOutput{Error: false}, }, { Name: "WriteColumn_VerifyPrice", Category: "write/column", Input: CommandInput{Op: "cat", Path: "products/20/price"}, Expected: ExpectedOutput{Exact: "99.99"}, }, { Name: "WriteColumn_ThroughFilter", Category: "write/pipeline", Input: CommandInput{Op: "echo", Path: "users/.filter/active/false/10/bio", Content: "Updated via filter"}, Expected: ExpectedOutput{Error: false}, }, { Name: "WriteColumn_VerifyThroughFilter", Category: "write/pipeline", Input: CommandInput{Op: "cat", Path: "users/10/bio"}, Expected: ExpectedOutput{Exact: "Updated via filter"}, }, }
WriteTestCases contains write test cases. These run after all read tests and stop on first failure.
Functions ¶
func SetupTestDB ¶
SetupTestDB is kept for backward compatibility - wraps GetTestDB Deprecated: Use GetTestDB instead
Types ¶
type CommandInput ¶
type CommandInput struct {
Op string // "ls", "cat", "echo", "mkdir", "touch", "rm"
Path string // Filesystem path (relative to mountpoint)
Content string // For echo/write operations
Args []string // Additional args (e.g., "-r" for rm)
}
CommandInput describes a filesystem operation to perform.
type CommandTestCase ¶
type CommandTestCase struct {
Name string // Test name for t.Run()
Category string // Grouping: "read", "write", "ddl", etc.
Input CommandInput // The operation to perform
Expected ExpectedOutput // What we expect
Skip string // Skip reason if not empty
}
CommandTestCase represents a single input/output test.
type DemoDataConfig ¶
type DemoDataConfig struct {
UserCount int // Number of users (default 100)
ProductCount int // Number of products (default 20)
CategoryCount int // Number of categories (fixed at 10)
OrderCount int // Number of orders (default 200)
}
DemoDataConfig controls the size of demo data for tests.
func DefaultDemoConfig ¶
func DefaultDemoConfig() DemoDataConfig
DefaultDemoConfig returns a smaller dataset suitable for fast tests.
type ExpectedOutput ¶
type ExpectedOutput struct {
// For exact match (after normalization)
Exact string
// For flexible matching
Contains []string // Output must contain all these
NotContains []string // Output must not contain these
LineCount int // Expected number of lines (-1 to skip check)
// For directory listings (always sorted before comparison)
Entries []string // Expected entry names
EntryCount int // Expected count (-1 to skip check)
// For JSON output
JSONFields map[string]any // Expected JSON fields (partial match)
JSONArray bool // Expect JSON array
JSONLength int // Expected array length (-1 to skip check)
// For error cases
Error bool // Expect an error
ErrorType string // "ENOENT", "EACCES", "EIO", etc.
// Normalization options
TrimWhitespace bool // Trim whitespace from output (default behavior)
}
ExpectedOutput describes what we expect from a command.
type MountMethod ¶
type MountMethod string
MountMethod indicates the filesystem mount method to use for tests
const ( MountMethodFUSE MountMethod = "fuse" // Linux FUSE (native) MountMethodNFS MountMethod = "nfs" // macOS NFS (native) MountMethodDocker MountMethod = "docker" // Docker container with Linux FUSE )
type TestDBResult ¶
type TestDBResult struct {
ConnStr string
Source TestDBSource
Cleanup func()
}
TestDBResult contains the connection info and cleanup function
func GetTestDB ¶
func GetTestDB(t *testing.T) *TestDBResult
GetTestDB returns a test database connection, trying local first then Docker. This is the primary function all integration tests should use.
func GetTestDBEmpty ¶
func GetTestDBEmpty(t *testing.T) *TestDBResult
GetTestDBEmpty returns a test database connection without seeding any data. Use this when tests will seed their own data (e.g., command tests with demo data).
type TestDBSource ¶
type TestDBSource int
TestDBSource indicates where the test database is running
const ( SourceLocal TestDBSource = iota // Local PostgreSQL SourceDocker // Docker container via testcontainers )