Stripped lots of boilerplate prepare for minimal test branch

This commit is contained in:
Jonas Hahn
2025-08-22 18:53:33 +02:00
parent 6f53cddf6c
commit 19e4834a9e
10 changed files with 11 additions and 219 deletions

View File

@@ -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

View File

@@ -4,24 +4,8 @@
# Main target # Main target
run: run:
go run ./cmd go run ./src
# Formatting the whole codebase
format:
gofmt -w ./src
gofmt -w ./cmd
# Test in verbose mode
test:
go test ./src -v
go test ./cmd -v
# Nix stuff # Nix stuff
nix-run: nix-run:
nix develop --command make run nix develop --command make run
nix-test:
nix develop --command make test
nix-format:
nix develop --command make format

View File

@@ -1,9 +0,0 @@
package main
import (
app "gitlab.gwdg.de/qrank/qrank/src"
)
func main() {
app.App()
}

View File

@@ -1,8 +0,0 @@
package main
import (
"testing"
)
// Dummy main function for testing
func TestMain(t *testing.T) {}

View File

@@ -1,4 +1,4 @@
package src package main
import ( import (
"database/sql" "database/sql"

View File

@@ -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")
}
}

View File

@@ -1,4 +1,4 @@
package src package main
import ( import (
"errors" "errors"

View File

@@ -1,30 +1,9 @@
package src package main
// 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).
import ( import (
"html/template" "html/template"
"log" "log"
"os" "os"
"path/filepath"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -47,27 +26,13 @@ const (
// ===================== Templates ===================== // ===================== Templates =====================
var tpl *template.Template var tpl = template.Must(template.New("").Funcs(template.FuncMap{
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") }, "fmtTime": func(t time.Time) string { return t.Local().Format("1000-01-01 10:10") },
}).ParseGlob(path)) }).ParseGlob("templates/*.html"))
}
// ===================== Main ===================== // ===================== Main =====================
func App() { func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile) log.SetFlags(log.LstdFlags | log.Lshortfile)
baseURL = os.Getenv("APP_BASE_URL") baseURL = os.Getenv("APP_BASE_URL")

View File

@@ -1,4 +1,4 @@
package src package main
import ( import (
"time" "time"

View File

@@ -1,4 +1,4 @@
package src package main
import ( import (
"crypto/rand" "crypto/rand"
@@ -104,7 +104,7 @@ func atoiSafe(s string) int {
} }
// minimal sscanf to avoid fmt import just for one call // 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" // Only supports "%d"
n := 0 n := 0
for i := 0; i < len(s); i++ { for i := 0; i < len(s); i++ {