Implement verified email registration and queued TLS delivery
Foundry Go checks / check (push) Successful in 1m52s
Foundry Go checks / check (pull_request) Successful in 1m53s

This commit is contained in:
2026-11-05 18:31:09 +08:00
parent 1b756d36ce
commit 0d0521f13d
8 changed files with 381 additions and 6 deletions
+30 -1
View File
@@ -7,10 +7,13 @@ import (
"crypto/sha256"
"database/sql"
"encoding/base64"
"encoding/hex"
"encoding/json"
"gitea.entroia.com/Zayden-Jung/foundry-core/internal/auth"
"net/http"
"net/http/httptest"
"os"
"regexp"
"testing"
_ "github.com/jackc/pgx/v5/stdlib"
@@ -29,7 +32,7 @@ func TestOnboardingAndSharing(t *testing.T) {
ctx := context.Background()
var raw [12]byte
rand.Read(raw[:])
project := "test-" + base64.RawURLEncoding.EncodeToString(raw[:])
project := "test-" + hex.EncodeToString(raw[:])
accounts := []string{}
tokens := []string{}
for range 3 {
@@ -52,9 +55,15 @@ func TestOnboardingAndSharing(t *testing.T) {
defer func() {
_, _ = db.ExecContext(ctx, "DELETE FROM workspaces WHERE project=$1", project)
_, _ = db.ExecContext(ctx, "DELETE FROM project_accounts WHERE project=$1", project)
_, _ = db.ExecContext(ctx, "DELETE FROM email_outbox WHERE project=$1", project)
}()
callbacks := map[string]string{project: "https://app.example.com/auth/callback"}
mux := http.NewServeMux()
authHandler, err := auth.New(db, callbacks, "https://api.example.com/auth/google/callback", "fixture", "fixture", &auth.EmailConfig{})
if err != nil {
t.Fatal(err)
}
mux.Handle("/", authHandler)
Register(mux, db, callbacks, "https://api.example.com", map[string]OnboardingConfig{
project: {RequireWorkspace: true, Questions: []Question{{Key: "purpose", Required: true}}},
})
@@ -110,6 +119,26 @@ func TestOnboardingAndSharing(t *testing.T) {
contentShare := call("POST", workspacePath+"/shares", tokens[0], response{"role": "editor", "material_id": material}, 201)
call("POST", base+"/shares/redeem", tokens[2], response{"token": contentShare["token"]}, 200)
call("GET", workspacePath, tokens[2], nil, 404)
email := "fixture@example.invalid"
password := "a-long-fixture-password"
call("POST", base+"/auth/register", "", response{"email": email, "password": password}, 202)
call("POST", base+"/auth/login", "", response{"email": email, "password": password}, 401)
var body string
if err := db.QueryRowContext(ctx, "SELECT body FROM email_outbox WHERE project=$1 AND recipient=$2 ORDER BY created_at DESC LIMIT 1", project, email).Scan(&body); err != nil {
t.Fatal(err)
}
match := regexp.MustCompile(`verify_email=([A-Za-z0-9_-]{43})`).FindStringSubmatch(body)
if len(match) != 2 {
t.Fatal("missing email verification link")
}
verified := call("POST", base+"/auth/verify-email", "", response{"token": match[1]}, 201)
call("POST", base+"/auth/verify-email", "", response{"token": match[1]}, 400)
call("GET", base+"/profile", verified["access_token"].(string), nil, 200)
call("POST", base+"/auth/login", "", response{"email": email, "password": "wrong-long-password"}, 401)
loggedIn := call("POST", base+"/auth/login", "", response{"email": email, "password": password}, 200)
call("POST", base+"/auth/logout", loggedIn["access_token"].(string), nil, 204)
call("GET", base+"/profile", loggedIn["access_token"].(string), nil, 401)
var count int
if err := db.QueryRowContext(ctx, "SELECT count(*) FROM material_grants WHERE project=$1 AND account_id=$2 AND material_id=$3", project, accounts[2], material).Scan(&count); err != nil || count != 1 {
t.Fatalf("material grant missing %v", err)