From 19e4834a9ed13a8bf3d2938f21f3ea0aee2f0bdb Mon Sep 17 00:00:00 2001 From: Jonas Hahn Date: Fri, 22 Aug 2025 18:53:33 +0200 Subject: [PATCH] Stripped lots of boilerplate prepare for minimal test branch --- .gitlab-ci.yml | 22 -------- Makefile | 18 +----- cmd/main.go | 9 --- cmd/main_test.go | 8 --- src/auth.go | 2 +- src/auth_test.go | 118 ---------------------------------------- src/handlers.go | 2 +- src/{app.go => main.go} | 45 ++------------- src/models.go | 2 +- src/utils.go | 4 +- 10 files changed, 11 insertions(+), 219 deletions(-) delete mode 100644 .gitlab-ci.yml delete mode 100644 cmd/main.go delete mode 100644 cmd/main_test.go delete mode 100644 src/auth_test.go rename src/{app.go => main.go} (55%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 5bac266..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,22 +0,0 @@ -# You can override the included template(s) by including variable overrides -# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings -# Secret Detection customization: https://docs.gitlab.com/user/application_security/secret_detection/pipeline/configure -# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings -# Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings -# Note that environment variables can be set in several places -# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence -:stages: -- test -:sast: - :stage: test -:include: -- :template: Security/SAST.gitlab-ci.yml -stages: -- test -- secret-detection -variables: - SECRET_DETECTION_ENABLED: 'true' -secret_detection: - stage: secret-detection -include: -- template: Security/Secret-Detection.gitlab-ci.yml diff --git a/Makefile b/Makefile index 6cf9cc6..4f33ad5 100644 --- a/Makefile +++ b/Makefile @@ -4,24 +4,8 @@ # Main target run: - go run ./cmd - -# Formatting the whole codebase -format: - gofmt -w ./src - gofmt -w ./cmd - -# Test in verbose mode -test: - go test ./src -v - go test ./cmd -v + go run ./src # Nix stuff nix-run: nix develop --command make run - -nix-test: - nix develop --command make test - -nix-format: - nix develop --command make format diff --git a/cmd/main.go b/cmd/main.go deleted file mode 100644 index 4f4aaa8..0000000 --- a/cmd/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - app "gitlab.gwdg.de/qrank/qrank/src" -) - -func main() { - app.App() -} diff --git a/cmd/main_test.go b/cmd/main_test.go deleted file mode 100644 index 8c946a2..0000000 --- a/cmd/main_test.go +++ /dev/null @@ -1,8 +0,0 @@ -package main - -import ( - "testing" -) - -// Dummy main function for testing -func TestMain(t *testing.T) {} diff --git a/src/auth.go b/src/auth.go index 604980a..9561dc3 100644 --- a/src/auth.go +++ b/src/auth.go @@ -1,4 +1,4 @@ -package src +package main import ( "database/sql" diff --git a/src/auth_test.go b/src/auth_test.go deleted file mode 100644 index 71bc16a..0000000 --- a/src/auth_test.go +++ /dev/null @@ -1,118 +0,0 @@ -package src - -import ( - "net/http" - "net/http/httptest" - "strings" - "testing" - - "github.com/gin-gonic/gin" - "gorm.io/driver/sqlite" - "gorm.io/gorm" -) - -// setupTestDB initializes an in-memory SQLite DB for tests -func setupTestDB(t *testing.T) *gorm.DB { - t.Helper() - d, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{}) - if err != nil { - t.Fatalf("failed to open test db: %v", err) - } - if err := d.AutoMigrate(&User{}, &Session{}); err != nil { - t.Fatalf("failed to migrate test db: %v", err) - } - db = d // override global - return d -} - -// TestFindOrCreateUserByHandle_Email -func TestFindOrCreateUserByHandle_Email(t *testing.T) { - setupTestDB(t) - - u, err := findOrCreateUserByHandle("test@example.com") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if u.Email != "test@example.com" { - t.Errorf("expected email test@example.com, got %s", u.Email) - } - - // Should return same user if called again - u2, err := findOrCreateUserByHandle("test@example.com") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if u.ID != u2.ID { - t.Errorf("expected same user ID, got %d and %d", u.ID, u2.ID) - } -} - -// TestFindOrCreateUserByHandle_Username -func TestFindOrCreateUserByHandle_Username(t *testing.T) { - setupTestDB(t) - - u, err := findOrCreateUserByHandle("PlayerOne") - if err != nil { - t.Fatalf("unexpected error: %v", err) - } - if !strings.Contains(u.Email, "@local") { - t.Errorf("expected placeholder email, got %s", u.Email) - } - if u.Username != "PlayerOne" { - t.Errorf("expected username PlayerOne, got %s", u.Username) - } -} - -// TestGetSessionUserAndRequireAuth -func TestGetSessionUserAndRequireAuth(t *testing.T) { - setupTestDB(t) - - // Create a user + session - user := &User{Email: "auth@example.com", Username: "authuser"} - if err := db.Create(user).Error; err != nil { - t.Fatalf("failed to create user: %v", err) - } - session := &Session{Token: "testtoken", UserID: user.ID} - if err := db.Create(session).Error; err != nil { - t.Fatalf("failed to create session: %v", err) - } - - // Make a Gin context with session cookie - w := httptest.NewRecorder() - c, _ := gin.CreateTestContext(w) - req, _ := http.NewRequest("GET", "/", nil) - req.AddCookie(&http.Cookie{Name: "session", Value: "testtoken"}) - c.Request = req - - // Should find user - u := getSessionUser(c) - if u == nil || u.Email != "auth@example.com" { - t.Errorf("expected user auth@example.com, got %+v", u) - } - - // Test requireAuth middleware with valid session - w = httptest.NewRecorder() - c, _ = gin.CreateTestContext(w) - req, _ = http.NewRequest("GET", "/", nil) - req.AddCookie(&http.Cookie{Name: "session", Value: "testtoken"}) - c.Request = req - c.Next() // prepare pipeline - mw := requireAuth() - mw(c) - - if c.IsAborted() { - t.Errorf("expected request to pass middleware, but it aborted") - } - - // Test requireAuth middleware with no cookie - w = httptest.NewRecorder() - c, _ = gin.CreateTestContext(w) - req, _ = http.NewRequest("GET", "/", nil) - c.Request = req - mw = requireAuth() - mw(c) - - if !c.IsAborted() { - t.Errorf("expected request to abort when no session, but it passed") - } -} diff --git a/src/handlers.go b/src/handlers.go index 9dc7a5f..6f2b988 100644 --- a/src/handlers.go +++ b/src/handlers.go @@ -1,4 +1,4 @@ -package src +package main import ( "errors" diff --git a/src/app.go b/src/main.go similarity index 55% rename from src/app.go rename to src/main.go index ad35aef..5e287c5 100644 --- a/src/app.go +++ b/src/main.go @@ -1,30 +1,9 @@ -package src - -// socqr-mvp: single-binary Go + Gin app with email-magic-link auth (debug links in logs), -// SQLite (switchable to Postgres), and four views: user view, enter scores, history, leaderboard. -// -// Quick start: -// go mod init socqr -// go get github.com/gin-gonic/gin gorm.io/gorm gorm.io/driver/sqlite gorm.io/driver/postgres -// go run . -// -// Env vars (optional): -// DATABASE_URL="postgres://user:pass@host:5432/dbname?sslmode=disable" // if set, uses Postgres; else SQLite file socqr.db -// APP_BASE_URL="http://localhost:8080" // used for magic-link generation (defaults to http://localhost:8080) -// APP_BIND=":8080" // server bind address (default :8080) -// APP_COOKIE_DOMAIN="" // cookie domain (default empty) -// -// Notes: -// - Magic link tokens are valid for 30 days and can be used multiple times during that window (for multi-device sign-in). -// - Session cookie is long-lived (~10 years). Delete cookie to sign out. -// - Minimal inline-styled HTML (no external CSS or JS) for fast load and pure-Go rendering. -// - QR prep: includes a Table model and routes under /t/:slug/enter to prefill table context (no scanner yet). +package main import ( "html/template" "log" "os" - "path/filepath" "time" "github.com/gin-gonic/gin" @@ -47,27 +26,13 @@ const ( // ===================== Templates ===================== -var tpl *template.Template - -func init() { - wd, err := os.Getwd() - if err != nil { - log.Fatal(err) - } - - if filepath.Base(wd) == "src" { - wd = filepath.Join(wd, "..") // adjust if running from src dir - } - path := filepath.Join(wd, "templates", "*.html") - - tpl = template.Must(template.New("").Funcs(template.FuncMap{ - "fmtTime": func(t time.Time) string { return t.Local().Format("1000-01-01 10:10") }, - }).ParseGlob(path)) -} +var tpl = template.Must(template.New("").Funcs(template.FuncMap{ + "fmtTime": func(t time.Time) string { return t.Local().Format("1000-01-01 10:10") }, +}).ParseGlob("templates/*.html")) // ===================== Main ===================== -func App() { +func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) baseURL = os.Getenv("APP_BASE_URL") diff --git a/src/models.go b/src/models.go index 4a82d07..d97053f 100644 --- a/src/models.go +++ b/src/models.go @@ -1,4 +1,4 @@ -package src +package main import ( "time" diff --git a/src/utils.go b/src/utils.go index c7cfe33..b5b9e1d 100644 --- a/src/utils.go +++ b/src/utils.go @@ -1,4 +1,4 @@ -package src +package main import ( "crypto/rand" @@ -104,7 +104,7 @@ func atoiSafe(s string) int { } // minimal sscanf to avoid fmt import just for one call -func fmtSscanf(s, format string, a *int) (int, error) { +func fmtSscanf(s, _ string, a *int) (int, error) { // Only supports "%d" n := 0 for i := 0; i < len(s); i++ {