This commit is contained in:
@@ -169,11 +169,36 @@ func TestHouseholdSubjectConsentLifecycleAndIsolation(t *testing.T) {
|
||||
if _, _, err := service.CreatePrivacyRequest(ctx, owner, subject.ID, "saas-integration-privacy-key", "saas-integration-privacy-conflict", conflictingPrivacy); !errors.Is(err, ErrIdempotencyConflict) {
|
||||
t.Fatalf("privacy idempotency conflict=%v", err)
|
||||
}
|
||||
correctedName := "集成长者-隐私更正"
|
||||
correction, created, err := service.CreatePrivacyRequest(ctx, owner, subject.ID, "saas-integration-correction-key", "saas-integration-correction", CreatePrivacyRequestInput{
|
||||
RequestType: "CORRECTION", Scope: map[string]any{"corrections": map[string]any{"preferred_name": correctedName}},
|
||||
})
|
||||
if err != nil || !created || correction.Status != "COMPLETED" || len(correction.Subtasks) != 1 || correction.Subtasks[0].Status != "COMPLETED" {
|
||||
t.Fatalf("correction request=%+v created=%v err=%v", correction, created, err)
|
||||
}
|
||||
correctedSubject, err := service.GetSubject(ctx, owner, subject.ID)
|
||||
if err != nil || correctedSubject.PreferredName != correctedName || correctedSubject.Version != subject.Version+1 {
|
||||
t.Fatalf("corrected subject=%+v err=%v", correctedSubject, err)
|
||||
}
|
||||
regranted, err := service.GrantConsent(ctx, editor, subject.ID, "saas-integration-consent-regrant", grantInput)
|
||||
if err != nil || !regranted.Effective {
|
||||
t.Fatalf("regrant consent=%+v err=%v", regranted, err)
|
||||
}
|
||||
withdrawal, created, err := service.CreatePrivacyRequest(ctx, owner, subject.ID, "saas-integration-withdrawal-key", "saas-integration-withdrawal", CreatePrivacyRequestInput{
|
||||
RequestType: "CONSENT_WITHDRAWAL", Scope: map[string]any{"consent_types": []string{ConsentStoryExtraction}},
|
||||
})
|
||||
if err != nil || !created || withdrawal.Status != "COMPLETED" || len(withdrawal.Subtasks) != 1 || withdrawal.Subtasks[0].Status != "COMPLETED" {
|
||||
t.Fatalf("withdrawal request=%+v created=%v err=%v", withdrawal, created, err)
|
||||
}
|
||||
consents, err = service.ListConsents(ctx, editor, subject.ID)
|
||||
if err != nil || len(consents) != 2 || consents[0].Effective {
|
||||
t.Fatalf("consents after batch withdrawal=%+v err=%v", consents, err)
|
||||
}
|
||||
if _, err := service.ListPrivacyRequests(ctx, editor, subject.ID); !errors.Is(err, ErrAccessDenied) {
|
||||
t.Fatalf("editor privacy list error=%v", err)
|
||||
}
|
||||
privacyRequests, err := service.ListPrivacyRequests(ctx, owner, subject.ID)
|
||||
if err != nil || len(privacyRequests) != 1 || privacyRequests[0].ID != privacyRequest.ID {
|
||||
if err != nil || len(privacyRequests) != 3 {
|
||||
t.Fatalf("privacy requests=%+v err=%v", privacyRequests, err)
|
||||
}
|
||||
if _, err := service.GetPrivacyRequest(ctx, outsider, privacyRequest.ID); !errors.Is(err, ErrAccessDenied) {
|
||||
@@ -206,15 +231,15 @@ func TestHouseholdSubjectConsentLifecycleAndIsolation(t *testing.T) {
|
||||
if err := pool.QueryRow(ctx, `SELECT count(*) FROM consent_snapshot WHERE subject_id=(SELECT id FROM subject_profile WHERE public_id=$1::uuid)`, subject.ID).Scan(&snapshotCount); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if snapshotCount != 2 {
|
||||
t.Fatalf("snapshot count=%d, want 2", snapshotCount)
|
||||
if snapshotCount != 4 {
|
||||
t.Fatalf("snapshot count=%d, want 4", snapshotCount)
|
||||
}
|
||||
var auditCount int
|
||||
if err := pool.QueryRow(ctx, `SELECT count(*) FROM audit_event WHERE request_id LIKE 'saas-integration-%'`).Scan(&auditCount); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if auditCount != 10 {
|
||||
t.Fatalf("audit count=%d, want 10", auditCount)
|
||||
if auditCount != 13 {
|
||||
t.Fatalf("audit count=%d, want 13", auditCount)
|
||||
}
|
||||
|
||||
_ = uuid.MustParse(household.ID)
|
||||
@@ -503,9 +528,11 @@ func TestContentReviewAndMemoryLifecycle(t *testing.T) {
|
||||
turnID, turnPublicID := uuid.NewString(), uuid.NewString()
|
||||
storyID, storyPublicID := uuid.NewString(), uuid.NewString()
|
||||
revisionID := uuid.NewString()
|
||||
var factPublicID string
|
||||
var memoryID string
|
||||
defer func() {
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM audit_event WHERE request_id LIKE 'content-integration-%'`)
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM content_review_signal WHERE resource_public_id=$1::uuid OR resource_public_id=$2::uuid`, storyPublicID, factPublicID)
|
||||
if memoryID != "" {
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM outbox_event WHERE aggregate_id=(SELECT id FROM curated_memory WHERE public_id=$1::uuid)`, memoryID)
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM memory_index_entry WHERE memory_id=(SELECT id FROM curated_memory WHERE public_id=$1::uuid)`, memoryID)
|
||||
@@ -513,6 +540,7 @@ func TestContentReviewAndMemoryLifecycle(t *testing.T) {
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM memory_version WHERE memory_id=(SELECT id FROM curated_memory WHERE public_id=$1::uuid)`, memoryID)
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM curated_memory WHERE public_id=$1::uuid`, memoryID)
|
||||
}
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM fact_candidate_revision WHERE fact_candidate_id IN (SELECT id FROM fact_candidate WHERE story_id=$1::uuid)`, storyID)
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM fact_candidate WHERE story_id=$1::uuid`, storyID)
|
||||
_, _ = pool.Exec(ctx, `DELETE FROM story_source WHERE story_revision_id IN (SELECT id FROM story_revision WHERE story_id=$1::uuid)`, storyID)
|
||||
_, _ = pool.Exec(ctx, `UPDATE story_draft SET current_revision_id=NULL WHERE id=$1::uuid`, storyID)
|
||||
@@ -580,10 +608,10 @@ func TestContentReviewAndMemoryLifecycle(t *testing.T) {
|
||||
VALUES($1::uuid,$2::uuid,0,15,'subject')`, revisionID, turnID); err != nil {
|
||||
t.Fatalf("insert story source: %v", err)
|
||||
}
|
||||
if _, err := pool.Exec(ctx, `
|
||||
if err := pool.QueryRow(ctx, `
|
||||
INSERT INTO fact_candidate(tenant_id,subject_id,story_id,fact_type,content,statement_basis,source_turn_ids,status,generation_version)
|
||||
VALUES('10000000-0000-0000-0000-000000000001','10000000-0000-0000-0000-000000000401',$1::uuid,'occupation',$2,'subject_statement',$3::jsonb,'CANDIDATE',1)`,
|
||||
storyID, "曾从事教师工作", sourceIDs); err != nil {
|
||||
VALUES('10000000-0000-0000-0000-000000000001','10000000-0000-0000-0000-000000000401',$1::uuid,'occupation',$2,'subject_statement',$3::jsonb,'CANDIDATE',1)
|
||||
RETURNING public_id::text`, storyID, "曾从事教师工作", sourceIDs).Scan(&factPublicID); err != nil {
|
||||
t.Fatalf("insert fact candidate: %v", err)
|
||||
}
|
||||
stories, err := service.ListSubjectStories(ctx, owner, "10000000-0000-0000-0000-000000000411", "CANDIDATE")
|
||||
@@ -594,12 +622,31 @@ func TestContentReviewAndMemoryLifecycle(t *testing.T) {
|
||||
if err != nil || len(story.Sources) != 1 || story.Sources[0].TurnID != turnPublicID || len(story.Facts) != 1 {
|
||||
t.Fatalf("story=%+v err=%v", story, err)
|
||||
}
|
||||
correctedFact := "曾在合成城市从事教师工作"
|
||||
confirmedFactStatus := "CONFIRMED"
|
||||
factErrorType := "PLACE_ERROR"
|
||||
fact, err := service.UpdateFactCandidate(ctx, owner, storyPublicID, factPublicID, "content-integration-fact-confirm", 1, UpdateFactCandidateInput{
|
||||
Content: &correctedFact, Status: &confirmedFactStatus, ErrorType: &factErrorType, Reason: "逐字核对长者原话",
|
||||
})
|
||||
if err != nil || fact.Content != correctedFact || fact.Status != "CONFIRMED" || fact.Version != 2 {
|
||||
t.Fatalf("confirmed fact=%+v err=%v", fact, err)
|
||||
}
|
||||
var factRevisionCount, factSignalCount int
|
||||
if err := pool.QueryRow(ctx, `SELECT (SELECT count(*) FROM fact_candidate_revision WHERE fact_candidate_id=(SELECT id FROM fact_candidate WHERE public_id=$1::uuid)),(SELECT count(*) FROM content_review_signal WHERE resource_type='fact' AND resource_public_id=$1::uuid)`, factPublicID).Scan(&factRevisionCount, &factSignalCount); err != nil || factRevisionCount != 2 || factSignalCount != 1 {
|
||||
t.Fatalf("fact revisions=%d signals=%d err=%v", factRevisionCount, factSignalCount, err)
|
||||
}
|
||||
if _, err := service.UpdateFactCandidate(ctx, owner, storyPublicID, factPublicID, "content-integration-fact-stale", 1, UpdateFactCandidateInput{Reason: "旧页面提交"}); !errors.Is(err, ErrVersionConflict) {
|
||||
t.Fatalf("stale fact review error=%v", err)
|
||||
}
|
||||
if _, err := service.UpdateFactCandidate(ctx, outsider, storyPublicID, factPublicID, "content-integration-fact-outsider", 2, UpdateFactCandidateInput{Reason: "越权提交"}); !errors.Is(err, ErrAccessDenied) {
|
||||
t.Fatalf("outsider fact review error=%v", err)
|
||||
}
|
||||
revisedContent := "受试者年轻时在合成城市任职教师,并明确这是本人陈述。"
|
||||
story, err = service.ReviseStory(ctx, owner, storyPublicID, revisedContent, "人工核对原话", "content-integration-revise", 1)
|
||||
story, err = service.ReviseStory(ctx, owner, storyPublicID, revisedContent, "人工核对原话", "content-integration-revise", 1, "PLACE_ERROR")
|
||||
if err != nil || story.Status != "IN_REVIEW" || story.Version != 2 || story.RevisionNo != 2 || len(story.Sources) != 1 {
|
||||
t.Fatalf("revised story=%+v err=%v", story, err)
|
||||
}
|
||||
if _, err := service.ReviseStory(ctx, owner, storyPublicID, "过期修改", "旧版本", "content-integration-stale", 1); !errors.Is(err, ErrVersionConflict) {
|
||||
if _, err := service.ReviseStory(ctx, owner, storyPublicID, "过期修改", "旧版本", "content-integration-stale", 1, ""); !errors.Is(err, ErrVersionConflict) {
|
||||
t.Fatalf("stale story revision error=%v", err)
|
||||
}
|
||||
story, memory, err := service.ConfirmStory(ctx, owner, storyPublicID, "来源与本人同意均已核验", "content-integration-confirm", 2)
|
||||
|
||||
Reference in New Issue
Block a user