0 { continue }
b, err := migrationsFS.ReadFile("migrations/" + name)
if err != nil { return fmt.Errorf("read %s: %w", name, err) }
if _, err := d.Exec(string(b)); err != nil { return fmt.Errorf("apply %s: %w", name, err) }
if _, err := d.Exec(`INSERT INTO schema_migrations (name) VALUES (?)`, name); err != nil {
return fmt.Errorf("record %s: %w", name, err)
}
}
return nil
}
''',
"internal/db/migrations/00001_init.sql": '''CREATE TABLE IF NOT EXISTS locations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
parent_id INTEGER NULL REFERENCES locations(id) ON DELETE CASCADE,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE UNIQUE INDEX IF NOT EXISTS locations_name_parent ON locations(name, IFNULL(parent_id, 0));
''',
"internal/models/location.go": '''package models
import (
"database/sql"
"fmt"
)
type Location struct {
ID int64
Name string
ParentID *int64
Depth int
}
type Store struct {
DB *sql.DB
}
func (s *Store) Create(name string, parentID *int64) (*Location, error) {
res, err := s.DB.Exec(`INSERT INTO locations (name, parent_id) VALUES (?, ?)`, name, parentID)
if err != nil { return nil, fmt.Errorf("insert location: %w", err) }
id, err := res.LastInsertId()
if err != nil { return nil, err }
return &Location{ID: id, Name: name, ParentID: parentID, Depth: 0}, nil
}
func (s *Store) Tree() ([]Location, error) {
rows, err := s.DB.Query(`WITH RECURSIVE tree AS (
SELECT id, name, parent_id, 0 AS depth FROM locations WHERE parent_id IS NULL
UNION ALL
SELECT l.id, l.name, l.parent_id, t.depth+1 FROM locations l JOIN tree t ON l.parent_id = t.id
) SELECT id, name, parent_id, depth FROM tree ORDER BY depth, name`)
if err != nil { return nil, fmt.Errorf("tree query: %w", err) }
defer rows.Close()
var out []Location
for rows.Next() {
var l Location
var pid sql.NullInt64
if err := rows.Scan(&l.ID, &l.Name, &pid, &l.Depth); err != nil { return nil, err }
if pid.Valid { l.ParentID = &pid.Int64 }
out = append(out, l)
}
return out, rows.Err()
}
''',
"internal/handlers/handlers.go": '''package handlers
import (
"embed"
"html/template"
"log"
"net/http"
"strconv"
"strings"
"locations/internal/models"
)
//go:embed templates/*.html
var templatesFS embed.FS
type Handlers struct {
store *models.Store
pageTmpl *template.Template
treeTmpl *template.Template
}
func New(store *models.Store) *Handlers {
pageTmpl := template.Must(template.ParseFS(templatesFS, "templates/page.html", "templates/tree.html"))
treeTmpl := template.Must(template.ParseFS(templatesFS, "templates/tree.html"))
return &Handlers{store: store, pageTmpl: pageTmpl, treeTmpl: treeTmpl}
}
func (h *Handlers) Routes() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("GET /", h.index)
mux.HandleFunc("POST /", h.create)
return mux
}
func (h *Handlers) index(w http.ResponseWriter, r *http.Request) {
locations, err := h.store.Tree()
if err != nil {
log.Printf("tree: %v", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := h.pageTmpl.Execute(w, locations); err != nil {
log.Printf("render page: %v", err)
}
}
func (h *Handlers) create(w http.ResponseWriter, r *http.Request) {
name := strings.TrimSpace(r.FormValue("name"))
if name == "" {
http.Error(w, "name is required", http.StatusBadRequest)
return
}
parentStr := r.FormValue("parent_id")
var parentID *int64
if parentStr != "" {
id, err := strconv.ParseInt(parentStr, 10, 64)
if err != nil {
http.Error(w, "bad parent_id", http.StatusBadRequest)
return
}
parentID = &id
}
if _, err := h.store.Create(name, parentID); err != nil {
log.Printf("create: %v", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
locations, err := h.store.Tree()
if err != nil {
log.Printf("tree after create: %v", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
// Return JUST the tree fragment for the HTMX swap (not a redirect, not a full page).
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
if err := h.treeTmpl.ExecuteTemplate(w, "tree", locations); err != nil {
log.Printf("render tree: %v", err)
}
}
''',
"internal/handlers/handlers_test.go": '''package handlers
import (
"database/sql"
"io"
"net/http"
"net/http/httptest"
"net/url"
"strconv"
"strings"
"testing"
"locations/internal/models"
_ "modernc.org/sqlite"
)
func newTestStore(t *testing.T) (*sql.DB, func()) {
t.Helper()
db, err := sql.Open("sqlite", ":memory:")
if err != nil { t.Fatalf("open: %v", err) }
_, err = db.Exec(`CREATE TABLE locations (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, parent_id INTEGER NULL REFERENCES locations(id), created_at DATETIME DEFAULT CURRENT_TIMESTAMP)`)
if err != nil { db.Close(); t.Fatalf("create table: %v", err) }
return db, func() { db.Close() }
}
func TestCreateRootAndChild(t *testing.T) {
db, cleanup := newTestStore(t)
defer cleanup()
store := &models.Store{DB: db}
h := New(store)
srv := httptest.NewServer(h.Routes())
defer srv.Close()
// POST a root location.
resp, err := http.PostForm(srv.URL+"/", url.Values{"name": {"BuildingA"}, "parent_id": {""}})
if err != nil { t.Fatalf("post root: %v", err) }
resp.Body.Close()
if resp.StatusCode != http.StatusOK {
t.Fatalf("post root status = %d, want 200", resp.StatusCode)
}
// Get the root's id from the DB.
var rootID int64
if err := db.QueryRow("SELECT id FROM locations WHERE name = 'BuildingA'").Scan(&rootID); err != nil {
t.Fatalf("query root id: %v", err)
}
// POST a child.
resp2, err := http.PostForm(srv.URL+"/", url.Values{"name": {"Floor1"}, "parent_id": {strconv.FormatInt(rootID, 10)}})
if err != nil { t.Fatalf("post child: %v", err) }
defer resp2.Body.Close()
if resp2.StatusCode != http.StatusOK {
t.Fatalf("post child status = %d, want 200", resp2.StatusCode)
}
body, _ := io.ReadAll(resp2.Body)
if !strings.Contains(string(body), "Floor1") {
t.Fatalf("response does not contain child name; got:\\n%s", body)
}
if !strings.Contains(string(body), "BuildingA") {
t.Fatalf("response does not contain root name (tree should include all); got:\\n%s", body)
}
}
''',
"internal/handlers/templates/page.html": '''
Locations
Locations
{{template "tree" .}}
''',
"internal/handlers/templates/tree.html": '''{{define "tree"}}
{{range .}}- {{.Name}}
{{end}}
{{end}}
''',
}
def _materialize(files, dest):
shutil.rmtree(dest, ignore_errors=True)
os.makedirs(dest, exist_ok=True)
for path, content in files.items():
fp = os.path.join(dest, path)
os.makedirs(os.path.dirname(fp), exist_ok=True)
with open(fp, "w") as f: f.write(content)
return dest
def _run(cmd, cwd, timeout=180):
try:
p = subprocess.run(cmd, cwd=cwd, shell=True, capture_output=True, text=True, timeout=timeout)
except subprocess.TimeoutExpired:
return "TIMEOUT"
return f"exit={p.returncode}\n{(p.stdout + p.stderr).strip()}"
def _free_port():
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM); s.bind(("", 0))
p = s.getsockname()[1]; s.close(); return p
def _has_real_app(work_dir):
main_go = os.path.join(work_dir, "cmd", "server", "main.go")
if not os.path.isfile(main_go): return False
with open(main_go) as f:
src = f.read()
if "ListenAndServe" not in src and "http.Server" not in src: return False
internal = os.path.join(work_dir, "internal")
if not os.path.isdir(internal): return False
pkg_dirs = [d for d in os.listdir(internal) if os.path.isdir(os.path.join(internal, d))]
return len(pkg_dirs) >= 3
def gates(work_dir):
"""Return a dict of gate_name -> bool."""
g = {}
if not _has_real_app(work_dir):
return {"build": False, "test": False, "serves": False, "persists": False}
b = _run("go build ./...", work_dir)
g["build"] = b.startswith("exit=0")
t = _run("go test ./...", work_dir)
g["test"] = t.startswith("exit=0")
# serves + persists
binpath = os.path.abspath(os.path.join(work_dir, "server"))
r = _run(f"go build -o {binpath} ./cmd/server", work_dir)
if not r.startswith("exit=0") or not os.path.isfile(binpath):
g["serves"] = False; g["persists"] = False; return g
port = _free_port()
dbpath = os.path.abspath(os.path.join(work_dir, f"bench_{port}.db"))
import os as _os
for ext in ("", "-wal", "-shm"):
if _os.path.exists(dbpath+ext): _os.remove(dbpath+ext)
env = dict(_os.environ, NOTES_PORT=str(port), NOTES_DB=dbpath)
try:
proc = subprocess.Popen([binpath], cwd=work_dir, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except Exception:
g["serves"] = False; g["persists"] = False; return g
try:
for _ in range(40):
try:
with socket.create_connection(("127.0.0.1", port), 0.25): break
except OSError: time.sleep(0.25)
else:
g["serves"] = False; g["persists"] = False; return g
base = f"http://127.0.0.1:{port}"
try:
with urllib.request.urlopen(base+"/", timeout=20) as resp:
if resp.status != 200: g["serves"]=False; g["persists"]=False; return g
html = resp.read().decode(errors="replace")
except Exception:
g["serves"]=False; g["persists"]=False; return g
g["serves"] = ('
0
conn.close()
except Exception:
pass
return g
finally:
proc.send_signal(signal.SIGTERM)
try: proc.wait(timeout=5)
except subprocess.TimeoutExpired: proc.kill()
for ext in ("", "-wal", "-shm"):
p = dbpath+ext
if os.path.exists(p):
try: os.remove(p)
except: pass
def validate(work_dir):
_materialize(REFERENCE, dest=work_dir)
r = _run("go mod tidy", work_dir)
print("go mod tidy:", r[:120])
b = _run("go build ./...", work_dir)
print(f"build: {'OK' if b.startswith('exit=0') else 'FAIL'}\n{b[-300:]}")
t = _run("go test ./...", work_dir)
print(f"test: {'OK' if t.startswith('exit=0') else 'FAIL'}\n{t[-300:]}")
g = gates(work_dir)
print(f"gates: {g}")
ok = all(g.values())
print(f"-> {'OK' if ok else 'BAD'}")
return ok
# Allow `python go_htmx.py` to run validate directly
if __name__ == "__main__":
import sys
_work_root = os.path.join(HERE, "..", "..", "..", "work")
wd = os.path.join(_work_root, "multitask_validate_go_htmx")
validate(wd)