//go:build integration package app import ( "database/sql" "os" "strings" "testing" "time" "github.com/go-sql-driver/mysql" ) func TestIntegrationMigrationsAreIdempotent(t *testing.T) { dsn := os.Getenv("GITOOCEAN_TEST_MYSQL_DSN") if dsn == "" { t.Skip("set GITOOCEAN_TEST_MYSQL_DSN to run integration migration tests") } cfg, err := mysql.ParseDSN(dsn) if err != nil { t.Fatal(err) } if cfg.DBName == "" || !strings.Contains(cfg.DBName, "test") { t.Fatalf("refusing to run integration test against non-test database %q", cfg.DBName) } db, err := openMySQLAndCreateDatabaseIfMissing(dsn) if err != nil { t.Fatal(err) } defer db.Close() if err := resetIntegrationDB(db); err != nil { t.Fatal(err) } if err := migrate(db); err != nil { t.Fatal(err) } if err := migrate(db); err != nil { t.Fatalf("second migrate should be idempotent: %v", err) } for _, table := range []string{"users", "auth_tokens", "repositories", "repository_collaborators", "pull_requests", "pull_request_comments"} { var name string if err := db.QueryRow("SHOW TABLES LIKE ?", table).Scan(&name); err != nil { t.Fatalf("expected table %s: %v", table, err) } } } func resetIntegrationDB(db *sql.DB) error { _, _ = db.Exec("SET FOREIGN_KEY_CHECKS=0") for _, table := range []string{"pull_request_comments", "pull_requests", "repository_collaborators", "auth_tokens", "repositories", "users"} { _, _ = db.Exec("DROP TABLE IF EXISTS " + quoteMySQLIdentifier(table)) } _, err := db.Exec("SET FOREIGN_KEY_CHECKS=1") return err } func TestIntegrationAuthTokenLifecycle(t *testing.T) { dsn := os.Getenv("GITOOCEAN_TEST_MYSQL_DSN") if dsn == "" { t.Skip("set GITOOCEAN_TEST_MYSQL_DSN to run integration auth tests") } db, err := openMySQLAndCreateDatabaseIfMissing(dsn) if err != nil { t.Fatal(err) } defer db.Close() if err := resetIntegrationDB(db); err != nil { t.Fatal(err) } if err := migrate(db); err != nil { t.Fatal(err) } if err := createUserDirect(db, "alice@example.com", "alice", "password123", true); err != nil { t.Fatal(err) } s := &Server{db: db, storage: t.TempDir(), publicURL: "http://example.test"} token, expires, err := s.createToken(1) if err != nil { t.Fatal(err) } if time.Until(expires) < tokenTTL-time.Minute { t.Fatalf("unexpected expiry: %s", expires) } user, err := s.userFromToken(token, "alice") if err != nil { t.Fatal(err) } if user.Username != "alice" || !user.IsAdmin { t.Fatalf("unexpected user from token: %#v", user) } if _, err := db.Exec("UPDATE auth_tokens SET revoked_at = UTC_TIMESTAMP() WHERE token_hash = ?", hashToken(token)); err != nil { t.Fatal(err) } if _, err := s.userFromToken(token, "alice"); err == nil { t.Fatal("expected revoked token to be rejected") } }