package tableDB import ( "fmt" "os" "testing" "time" "apigo.cc/go/cast" "apigo.cc/go/log" ) func setupDB(t *testing.T, dbFile string) *TableDB { _ = os.Remove(dbFile) logger := log.DefaultLogger logger.SetLevel(log.ERROR) dbInst := GetDB("sqlite://"+dbFile, logger).Auth(SystemUserID) // Bootstrap system tables _ = dbInst.syncSchema("") return dbInst } func TestBootstrapAndSync(t *testing.T) { dbFile := "test_bootstrap.db" _ = os.Remove(dbFile) dbInst := GetDB("sqlite://"+dbFile, log.DefaultLogger).Auth(SystemUserID) defer os.Remove(dbFile) // 1. Bootstrap: sync empty DSL, should still create system tables err := dbInst.syncSchema("") if err != nil { t.Fatalf("Bootstrap failed: %v", err) } // Verify system tables in cache if GlobalCache.GetTable("_Table") == nil { t.Errorf("_Table not found in cache after bootstrap") } if GlobalCache.GetTable("_Field") == nil { t.Errorf("_Field not found in cache after bootstrap") } // 2. Create business table via API err = dbInst.Table("users").SetField( FieldSchema{Name: "name", Type: "v64", IsIndex: true}, FieldSchema{Name: "age", Type: "i"}, ) if err != nil { t.Fatalf("Create table failed: %v", err) } // Verify business table if GlobalCache.GetTable("users") == nil { t.Errorf("users table not found in cache after sync") } fields, _ := dbInst.Table("users").Fields() if len(fields) < 3 { // id, name, age t.Errorf("Expected at least 3 fields for users, got %v", fields) } } func TestAPIDrivenSchema(t *testing.T) { dbFile := "test_api_schema.db" dbInst := setupDB(t, dbFile) defer os.Remove(dbFile) app := dbInst.Auth("admin") // 1. Create table and add fields via SetField API err := app.Table("tasks").SetField( FieldSchema{Name: "title", Type: "v100", Memo: "Task title"}, FieldSchema{Name: "done", Type: "b", IsIndex: true}, ) if err != nil { t.Fatalf("Failed to create table/fields via API: %v", err) } // 2. Verify table works taskTable := app.Table("tasks") err = taskTable.Set(map[string]any{ "title": "Fix tests", "done": false, }) if err != nil { t.Fatalf("Failed to insert into tasks table: %v", err) } res, _ := taskTable.List(nil) if len(res) != 1 || res[0]["title"] != "Fix tests" { t.Errorf("Query from tasks failed: %v", res) } } func TestDataOperations(t *testing.T) { dbFile := "test_data_ops.db" dbInst := setupDB(t, dbFile) defer os.Remove(dbFile) _ = dbInst.Table("products").SetField( FieldSchema{Name: "name", Type: "v64", IsIndex: true}, FieldSchema{Name: "price", Type: "i"}, ) app := dbInst.Auth("admin") table := app.Table("products") // Create (Batch) err := table.Set( map[string]any{"name": "Laptop", "price": 1000}, map[string]any{"id": "p2", "name": "Mouse", "price": 50}, ) if err != nil { t.Fatalf("Batch set failed: %v", err) } laptop, _ := table.List(map[string]any{"name": "Laptop"}) if len(laptop) == 0 { t.Fatalf("Laptop not created") } laptopID := cast.String(laptop[0]["id"]) // Read p1, _ := table.Get("p2") if p1 == nil || p1["name"] != "Mouse" { t.Errorf("Get p2 failed") } // Update _ = table.Set(map[string]any{"id": "p2", "price": 45}) p1_updated, _ := table.Get("p2") if cast.Int(p1_updated["price"]) != 45 { t.Errorf("Update p2 failed, got %v", p1_updated["price"]) } // List & Filter list, _ := table.List(map[string]any{"price >": 100}) if len(list) != 1 || list[0]["name"] != "Laptop" { t.Errorf("List/Filter failed: %v", list) } // Count cnt, _ := table.Count(nil) if cnt != 2 { t.Errorf("Count failed: %d", cnt) } // Delete (Batch) err = table.Remove("p2", laptopID) if err != nil { t.Fatalf("Batch remove failed: %v", err) } p1_removed, _ := table.Get("p2") if p1_removed != nil { t.Errorf("Expected p2 to be removed") } // Verify shadow delete (actual table has _deleted suffix) raw, _ := dbInst.GetRawDB() res := raw.Query("SELECT name FROM sqlite_master WHERE name='products_deleted'") if res.MapOnR1()["name"] == nil { t.Errorf("Shadow delete table products_deleted not found") } } func TestPermissionsAndAuth(t *testing.T) { dbFile := "test_auth.db" dbInst := setupDB(t, dbFile) defer os.Remove(dbFile) _ = dbInst.Table("secrets").SetField( FieldSchema{Name: "content", Type: "t"}, FieldSchema{Name: "creator", Type: "v64"}, ) _ = dbInst.SetTable(TableSchema{Name: "secrets", EnableRLS: true}) user1 := dbInst.Auth("user1") user2 := dbInst.Auth("user2") // user1 creates a secret _ = user1.Table("secrets").Set(map[string]any{"id": "s1", "content": "user1-secret"}) // user2 tries to get it _, err := user2.Table("secrets").Get("s1") if err == nil { t.Errorf("user2 should not have permission to get user1 secret") } // system gets it sys := dbInst.Auth(SystemUserID) s, err := sys.Table("secrets").Get("s1") if err != nil || s == nil { t.Errorf("system should have permission: %v", err) } // Test _Policy: Grant access to specific ID via condition err = sys.Table("_Policy").Set(map[string]any{ "userID": "user2", "type": "table", "targets": []string{"secrets"}, "action": "read", "condition": "id = ?", "conditionArgs": []any{"s1"}, }) if err != nil { t.Fatalf("Failed to set policy: %v", err) } // Now user2 should be able to get it s, err = user2.Table("secrets").Get("s1") if err != nil || s == nil { t.Errorf("user2 should now have permission via policy: %v", err) } } func TestInheritance(t *testing.T) { dbFile := "test_inherit.db" dbInst := setupDB(t, dbFile) defer os.Remove(dbFile) _ = dbInst.Table("docs").SetField( FieldSchema{Name: "title", Type: "v64"}, FieldSchema{Name: "creator", Type: "v64"}, ) _ = dbInst.SetTable(TableSchema{Name: "docs", EnableRLS: true}) sys := dbInst.Auth(SystemUserID) _ = sys.Table("docs").Set(map[string]any{"id": "d1", "title": "secret doc", "creator": "boss"}) // manager inherits boss _ = sys.Table("_Policy").Set(map[string]any{ "userID": "manager", "type": "inherit", "targets": []string{"boss"}, }) // boss can read docs _ = sys.Table("_Policy").Set(map[string]any{ "userID": "boss", "type": "table", "targets": []string{"docs"}, "action": "read", }) manager := dbInst.Auth("manager") d, err := manager.Table("docs").Get("d1") if err != nil || d == nil { t.Errorf("manager should inherit boss's permission to read docs: %v", err) } } func TestPolicyInterfaces(t *testing.T) { dbFile := "test_policy_api.db" dbInst := setupDB(t, dbFile) defer os.Remove(dbFile) sys := dbInst.Auth(SystemUserID) _ = sys.Table("data").SetField(FieldSchema{Name: "val", Type: "v10"}) _ = sys.SetTable(TableSchema{Name: "data", EnableRLS: true}) // 1. Normal user tries to set inherit policy (should fail) user1 := dbInst.Auth("user1") err := user1.SetPolicy(PolicySchema{ UserID: "user2", Type: "inherit", Targets: []string{"user1"}, }) if err == nil { t.Errorf("Normal user should not be able to set inherit policy") } // 2. Normal user tries to set table policy for table they don't have full access to (should fail) err = user1.SetPolicy(PolicySchema{ UserID: "user2", Type: "table", Targets: []string{"data"}, Action: "read", }) if err == nil { t.Errorf("User without full access to table should not be able to set policy for it") } // 3. System grants user1 full access to 'data' _ = sys.SetPolicy(PolicySchema{ UserID: "user1", Type: "table", Targets: []string{"data"}, Action: "full", }) // 4. Now user1 sets policy for user2 on 'data' (should succeed) err = user1.SetPolicy(PolicySchema{ UserID: "user2", Type: "table", Targets: []string{"data"}, Action: "read", }) if err != nil { t.Errorf("User with full access should be able to set policy: %v", err) } // 5. List policies pols, err := user1.ListPolicy(map[string]any{"userID": "user2"}) if err != nil || len(pols) == 0 || pols[0].UserID != "user2" { t.Errorf("ListPolicy failed: %v, %v", err, pols) } } func TestHooks(t *testing.T) { dbFile := "test_hooks.db" dbInst := setupDB(t, dbFile) defer os.Remove(dbFile) var tableCreated string var rowsUpdated int dbInst.hooks.OnCreatedTable = func(tableName string, record map[string]any) { tableCreated = tableName } dbInst.hooks.OnUpdatedRows = func(tableName string, count int) { rowsUpdated += count } app := dbInst.Auth("admin") // Trigger OnCreatedTable _ = app.Table("hook_test").SetField(FieldSchema{Name: "val", Type: "v10"}) if tableCreated != "hook_test" { t.Errorf("OnCreatedTable hook failed, got %s", tableCreated) } // Trigger OnUpdatedRows _ = app.Table("hook_test").Set(map[string]any{"id": "1", "val": "a"}) if rowsUpdated != 1 { t.Errorf("OnUpdatedRows hook failed, got %d", rowsUpdated) } } func TestQueryValidationAndInjection(t *testing.T) { dbFile := "test_query.db" dbInst := setupDB(t, dbFile) defer os.Remove(dbFile) _ = dbInst.Table("safe_table").SetField(FieldSchema{Name: "name", Type: "v64"}) app := dbInst.Auth("user1") // Valid query req := QueryRequest{ Select: []string{"id", "name"}, OrderBy: "name DESC", } _, err := app.Table("safe_table").Query(req) if err != nil { t.Errorf("Valid query failed: %v", err) } // SQL Injection in Select req.Select = []string{"name` FROM safe_table; --"} _, err = app.Table("safe_table").Query(req) if err == nil { t.Errorf("Should fail for injection in Select") } } func BenchmarkTableSet(b *testing.B) { logger := log.DefaultLogger logger.SetLevel(log.ERROR) dbFile := fmt.Sprintf("bench_ops_%d.db", time.Now().UnixNano()) defer os.Remove(dbFile) app := GetDB("sqlite://"+dbFile, logger).Auth(SystemUserID) _ = app.syncSchema("") _ = app.Table("bench_ops").SetField( FieldSchema{Name: "name", Type: "v50", IsIndex: true}, FieldSchema{Name: "val", Type: "i"}, ) table := app.Table("bench_ops") b.ResetTimer() for i := 0; i < b.N; i++ { _ = table.Set(map[string]any{ "name": "user_" + cast.String(i), "val": i, }) } }