chore: establish local platform baseline
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
package privacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"data-terminal-cloud/internal/database"
|
||||
"data-terminal-cloud/internal/outbox"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
const (
|
||||
privacyAccessRequestID = "90000000-0000-0000-0000-000000000701"
|
||||
privacyAccessRequestPublicID = "90000000-0000-0000-0000-000000000711"
|
||||
privacyWrongRequestID = "90000000-0000-0000-0000-000000000702"
|
||||
)
|
||||
|
||||
func TestAccessProcessorCompletesScopedInventoryIdempotently(t *testing.T) {
|
||||
databaseURL := os.Getenv("TEST_DATABASE_URL")
|
||||
if databaseURL == "" {
|
||||
t.Skip("TEST_DATABASE_URL is not set")
|
||||
}
|
||||
ctx := context.Background()
|
||||
pool, err := database.Open(ctx, databaseURL, "privacy-access-test")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer pool.Close()
|
||||
cleanupPrivacyAccessFixture(t, ctx, pool)
|
||||
defer cleanupPrivacyAccessFixture(t, context.Background(), pool)
|
||||
|
||||
if _, err := pool.Exec(ctx, `INSERT INTO privacy_request (
|
||||
id,public_id,tenant_id,subject_id,request_type,scope,requested_by,idempotency_key,request_digest
|
||||
) VALUES (
|
||||
$1::uuid,$2::uuid,'10000000-0000-0000-0000-000000000001','10000000-0000-0000-0000-000000000401',
|
||||
'ACCESS','{"categories":["profile","conversations","unknown",7]}'::jsonb,
|
||||
'10000000-0000-0000-0000-000000000101','privacy-access-integration',repeat('7',64)
|
||||
)`, privacyAccessRequestID, privacyAccessRequestPublicID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := pool.Exec(ctx, `INSERT INTO privacy_subtask (
|
||||
public_id,privacy_request_id,subsystem,target_type,target_ref
|
||||
) VALUES (
|
||||
'90000000-0000-0000-0000-000000000721',$1::uuid,'postgresql','subject_data_inventory','subject'
|
||||
)`, privacyAccessRequestID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
processor, err := NewAccessProcessor(pool)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := processor.Handle(ctx, outbox.Event{AggregateID: privacyAccessRequestID}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var status, taskStatus string
|
||||
var attempts int
|
||||
var resultBody []byte
|
||||
if err := pool.QueryRow(ctx, `SELECT pr.status,pr.result_summary,ps.status,ps.attempts
|
||||
FROM privacy_request pr JOIN privacy_subtask ps ON ps.privacy_request_id=pr.id WHERE pr.id=$1::uuid`, privacyAccessRequestID).Scan(&status, &resultBody, &taskStatus, &attempts); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var result struct {
|
||||
InventoryCounts map[string]int64 `json:"inventory_counts"`
|
||||
ContentIncluded bool `json:"content_included"`
|
||||
}
|
||||
if err := json.Unmarshal(resultBody, &result); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if status != "COMPLETED" || taskStatus != "COMPLETED" || attempts != 1 || result.ContentIncluded || len(result.InventoryCounts) != 2 || result.InventoryCounts["profile"] != 1 || result.InventoryCounts["conversations"] != 0 {
|
||||
t.Fatalf("status=%s task=%s attempts=%d result=%#v", status, taskStatus, attempts, result)
|
||||
}
|
||||
if err := processor.Handle(ctx, outbox.Event{AggregateID: privacyAccessRequestID}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var audits int
|
||||
if err := pool.QueryRow(ctx, `SELECT count(*) FROM audit_event WHERE action='privacy.access.complete' AND resource_id=$1::uuid`, privacyAccessRequestPublicID).Scan(&audits); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if audits != 1 {
|
||||
t.Fatalf("privacy access audits=%d", audits)
|
||||
}
|
||||
if err := processor.Handle(ctx, outbox.Event{AggregateID: "90000000-0000-0000-0000-000000000799"}); err != nil {
|
||||
t.Fatalf("missing request error=%v", err)
|
||||
}
|
||||
|
||||
if _, err := pool.Exec(ctx, `INSERT INTO privacy_request (
|
||||
id,public_id,tenant_id,subject_id,request_type,scope,requested_by,idempotency_key,request_digest
|
||||
) VALUES (
|
||||
$1::uuid,'90000000-0000-0000-0000-000000000712','10000000-0000-0000-0000-000000000001',
|
||||
'10000000-0000-0000-0000-000000000401','CORRECTION','{}'::jsonb,
|
||||
'10000000-0000-0000-0000-000000000101','privacy-access-wrong-type',repeat('8',64)
|
||||
)`, privacyWrongRequestID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := processor.Handle(ctx, outbox.Event{AggregateID: privacyWrongRequestID}); err == nil || !strings.Contains(err.Error(), "CORRECTION") {
|
||||
t.Fatalf("wrong request type error=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func cleanupPrivacyAccessFixture(t *testing.T, ctx context.Context, pool *pgxpool.Pool) {
|
||||
t.Helper()
|
||||
statements := []string{
|
||||
`DELETE FROM audit_event WHERE action='privacy.access.complete' AND resource_id IN ('90000000-0000-0000-0000-000000000711'::uuid,'90000000-0000-0000-0000-000000000712'::uuid)`,
|
||||
`DELETE FROM privacy_request WHERE id IN ('90000000-0000-0000-0000-000000000701'::uuid,'90000000-0000-0000-0000-000000000702'::uuid)`,
|
||||
}
|
||||
for _, statement := range statements {
|
||||
if _, err := pool.Exec(ctx, statement); err != nil {
|
||||
t.Fatalf("cleanup privacy access fixture: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user