Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
99 lines
2.7 KiB
Go
99 lines
2.7 KiB
Go
package feeds
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
)
|
|
|
|
// TestRotateExportFeedPublicToken creates an ephemeral sandbox company (never A1 /
|
|
// Platform Demo), rotates the export public token, and asserts revoke semantics.
|
|
func TestRotateExportFeedPublicToken(t *testing.T) {
|
|
dsn := strings.TrimSpace(os.Getenv("DATABASE_URL"))
|
|
if dsn == "" {
|
|
t.Skip("DATABASE_URL not set")
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
pg, err := pgxpool.New(ctx, dsn)
|
|
if err != nil {
|
|
t.Fatalf("pool: %v", err)
|
|
}
|
|
defer pg.Close()
|
|
|
|
companyID := uuid.New()
|
|
prefix := companyID.String()[:8]
|
|
_, err = pg.Exec(ctx, `INSERT INTO companies (id, name) VALUES ($1, $2)`,
|
|
companyID, "export-rotate-"+prefix)
|
|
if err != nil {
|
|
t.Fatalf("seed company: %v", err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_, _ = pg.Exec(context.Background(), `DELETE FROM companies WHERE id = $1`, companyID)
|
|
})
|
|
|
|
svc := &Service{Pool: pg}
|
|
created, err := svc.CreateExportFeed(ctx, companyID, CreateExportInput{
|
|
Name: "rotate-smoke-" + prefix,
|
|
Format: "xml",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("CreateExportFeed: %v", err)
|
|
}
|
|
oldTok, _ := created["public_token"].(string)
|
|
if !validPublicToken(oldTok) || len(oldTok) != 64 {
|
|
t.Fatalf("create public_token=%q want 64-hex", oldTok)
|
|
}
|
|
feedID, ok := created["id"].(uuid.UUID)
|
|
if !ok {
|
|
idStr := fmt.Sprint(created["id"])
|
|
feedID, err = uuid.Parse(idStr)
|
|
if err != nil {
|
|
t.Fatalf("export id: %v (%v)", err, created["id"])
|
|
}
|
|
}
|
|
|
|
rotated, err := svc.RotateExportFeedPublicToken(ctx, companyID, feedID)
|
|
if err != nil {
|
|
t.Fatalf("RotateExportFeedPublicToken: %v", err)
|
|
}
|
|
newTok, _ := rotated["public_token"].(string)
|
|
if !validPublicToken(newTok) || len(newTok) != 64 {
|
|
t.Fatalf("rotated public_token=%q want 64-hex", newTok)
|
|
}
|
|
if newTok == oldTok {
|
|
t.Fatal("rotate must replace public_token")
|
|
}
|
|
|
|
got, err := svc.GetExportFeed(ctx, companyID, feedID)
|
|
if err != nil {
|
|
t.Fatalf("GetExportFeed: %v", err)
|
|
}
|
|
if fmt.Sprint(got["public_token"]) != newTok {
|
|
t.Fatalf("persisted token=%v want %s", got["public_token"], newTok)
|
|
}
|
|
|
|
var byOld int
|
|
err = pg.QueryRow(ctx, `
|
|
SELECT COUNT(*) FROM export_feeds
|
|
WHERE company_id = $1 AND public_token = $2`, companyID, oldTok).Scan(&byOld)
|
|
if err != nil {
|
|
t.Fatalf("count old token: %v", err)
|
|
}
|
|
if byOld != 0 {
|
|
t.Fatal("old public_token still present after rotate (not revoked)")
|
|
}
|
|
|
|
otherCompany := uuid.New()
|
|
_, err = svc.RotateExportFeedPublicToken(ctx, otherCompany, feedID)
|
|
if err == nil || !strings.Contains(err.Error(), "not found") {
|
|
t.Fatalf("cross-tenant rotate err=%v want not found", err)
|
|
}
|
|
}
|