// Copyright (c) 2025-2026 Probo Inc . // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR // OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. // Package factory provides Rails-like test data factories using gofakeit. package factory import ( "bytes" "encoding/json" "fmt" "maps" "net/http" "strings" "github.com/brianvoe/gofakeit/v7" "github.com/stretchr/testify/require" "go.probo.inc/probo/e2e/internal/testutil" ) func SafeName(prefix string) string { return fmt.Sprintf("%s %s", prefix, gofakeit.LetterN(8)) } func SafeEmail() string { return fmt.Sprintf("%s@example.com", strings.ToLower(gofakeit.LetterN(12))) } type Attrs map[string]any func (a Attrs) get(key string, defaultVal any) any { if a == nil { return defaultVal } if v, ok := a[key]; ok { return v } return defaultVal } func (a Attrs) getString(key string, defaultVal string) string { if v, ok := a.get(key, defaultVal).(string); ok { return v } return defaultVal } func (a Attrs) getStringPtr(key string) *string { if a == nil { return nil } if v, ok := a[key]; ok { if s, ok := v.(string); ok { return &s } } return nil } func (a Attrs) getInt(key string, defaultVal int) int { if a == nil { return defaultVal } if v, ok := a[key]; ok { switch val := v.(type) { case int: return val case int64: return int(val) case float64: return int(val) } } return defaultVal } func (a Attrs) getBool(key string, defaultVal bool) bool { if a == nil { return defaultVal } if v, ok := a[key]; ok { if b, ok := v.(bool); ok { return b } } return defaultVal } func CreateUser(c *testutil.Client, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateUserInput!) { createUser(input: $input) { profileEdge { node { id } } } } ` input := map[string]any{ "organizationId": c.GetOrganizationID(), "emailAddress": a.getString("emailAddress", SafeEmail()), "fullName": a.getString("fullName", SafeName("User")), "role": a.getString("role", "EMPLOYEE"), "kind": "EMPLOYEE", "additionalEmailAddresses": []string{}, } if position := a.getStringPtr("position"); position != nil { input["position"] = *position } var result struct { CreateUser struct { ProfileEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"profileEdge"` } `json:"createUser"` } err := c.ExecuteConnect(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createUser mutation failed") return result.CreateUser.ProfileEdge.Node.ID } func CreateThirdParty(c *testutil.Client, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateThirdPartyInput!) { createThirdParty(input: $input) { thirdPartyEdge { node { id } } } } ` input := map[string]any{ "organizationId": c.GetOrganizationID().String(), "name": a.getString("name", SafeName("ThirdParty")), } if desc := a.getStringPtr("description"); desc != nil { input["description"] = *desc } if url := a.getStringPtr("websiteUrl"); url != nil { input["websiteUrl"] = *url } if cat := a.getStringPtr("category"); cat != nil { input["category"] = *cat } var result struct { CreateThirdParty struct { ThirdPartyEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"thirdPartyEdge"` } `json:"createThirdParty"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createThirdParty mutation failed") return result.CreateThirdParty.ThirdPartyEdge.Node.ID } func CreateFramework(c *testutil.Client, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateFrameworkInput!) { createFramework(input: $input) { frameworkEdge { node { id } } } } ` input := map[string]any{ "organizationId": c.GetOrganizationID().String(), "name": a.getString("name", SafeName("Framework")), } if desc := a.getStringPtr("description"); desc != nil { input["description"] = *desc } var result struct { CreateFramework struct { FrameworkEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"frameworkEdge"` } `json:"createFramework"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createFramework mutation failed") return result.CreateFramework.FrameworkEdge.Node.ID } func CreateControl(c *testutil.Client, frameworkID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateControlInput!) { createControl(input: $input) { controlEdge { node { id } } } } ` input := map[string]any{ "frameworkId": frameworkID, "name": a.getString("name", SafeName("Control")), "description": a.getString("description", "Test control description"), "sectionTitle": a.getString("sectionTitle", fmt.Sprintf("Section %s", gofakeit.LetterN(3))), "bestPractice": a.getBool("bestPractice", true), "maturityLevel": a.getString("maturityLevel", "INITIAL"), } if justification := a.getStringPtr("notImplementedJustification"); justification != nil { input["notImplementedJustification"] = *justification } var result struct { CreateControl struct { ControlEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"controlEdge"` } `json:"createControl"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createControl mutation failed") return result.CreateControl.ControlEdge.Node.ID } func CreateMeasure(c *testutil.Client, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateMeasureInput!) { createMeasure(input: $input) { measureEdge { node { id } } } } ` input := map[string]any{ "organizationId": c.GetOrganizationID().String(), "name": a.getString("name", SafeName("Measure")), "category": a.getString("category", "POLICY"), } if desc := a.getStringPtr("description"); desc != nil { input["description"] = *desc } var result struct { CreateMeasure struct { MeasureEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"measureEdge"` } `json:"createMeasure"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createMeasure mutation failed") return result.CreateMeasure.MeasureEdge.Node.ID } func CreateTask(c *testutil.Client, measureID *string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateTaskInput!) { createTask(input: $input) { taskEdge { node { id } } } } ` input := map[string]any{ "organizationId": c.GetOrganizationID().String(), "name": a.getString("name", SafeName("Task")), "priority": a.getString("priority", "MEDIUM"), } if measureID != nil { input["measureId"] = *measureID } if desc := a.getStringPtr("description"); desc != nil { input["description"] = *desc } var result struct { CreateTask struct { TaskEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"taskEdge"` } `json:"createTask"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createTask mutation failed") return result.CreateTask.TaskEdge.Node.ID } func CreateRisk(c *testutil.Client, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateRiskInput!) { createRisk(input: $input) { riskEdge { node { id } } } } ` input := map[string]any{ "organizationId": c.GetOrganizationID().String(), "name": a.getString("name", SafeName("Risk")), "category": a.getString("category", "SECURITY"), "treatment": a.getString("treatment", "MITIGATED"), "inherentLikelihood": a.getInt("inherentLikelihood", 2), "inherentImpact": a.getInt("inherentImpact", 2), } if desc := a.getStringPtr("description"); desc != nil { input["description"] = *desc } var result struct { CreateRisk struct { RiskEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"riskEdge"` } `json:"createRisk"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRisk mutation failed") return result.CreateRisk.RiskEdge.Node.ID } type ThirdPartyBuilder struct { client *testutil.Client attrs Attrs } func NewThirdParty(c *testutil.Client) *ThirdPartyBuilder { return &ThirdPartyBuilder{client: c, attrs: Attrs{}} } func (b *ThirdPartyBuilder) WithName(name string) *ThirdPartyBuilder { b.attrs["name"] = name return b } func (b *ThirdPartyBuilder) WithDescription(desc string) *ThirdPartyBuilder { b.attrs["description"] = desc return b } func (b *ThirdPartyBuilder) WithWebsiteUrl(url string) *ThirdPartyBuilder { b.attrs["websiteUrl"] = url return b } func (b *ThirdPartyBuilder) WithCategory(category string) *ThirdPartyBuilder { b.attrs["category"] = category return b } func (b *ThirdPartyBuilder) Create() string { return CreateThirdParty(b.client, b.attrs) } type FrameworkBuilder struct { client *testutil.Client attrs Attrs } func NewFramework(c *testutil.Client) *FrameworkBuilder { return &FrameworkBuilder{client: c, attrs: Attrs{}} } func (b *FrameworkBuilder) WithName(name string) *FrameworkBuilder { b.attrs["name"] = name return b } func (b *FrameworkBuilder) WithDescription(desc string) *FrameworkBuilder { b.attrs["description"] = desc return b } func (b *FrameworkBuilder) Create() string { return CreateFramework(b.client, b.attrs) } type ControlBuilder struct { client *testutil.Client frameworkID string attrs Attrs } func NewControl(c *testutil.Client, frameworkID string) *ControlBuilder { return &ControlBuilder{client: c, frameworkID: frameworkID, attrs: Attrs{}} } func (b *ControlBuilder) WithName(name string) *ControlBuilder { b.attrs["name"] = name return b } func (b *ControlBuilder) WithDescription(desc string) *ControlBuilder { b.attrs["description"] = desc return b } func (b *ControlBuilder) WithSectionTitle(title string) *ControlBuilder { b.attrs["sectionTitle"] = title return b } func (b *ControlBuilder) WithStatus(status string) *ControlBuilder { b.attrs["status"] = status return b } func (b *ControlBuilder) WithBestPractice(bestPractice bool) *ControlBuilder { b.attrs["bestPractice"] = bestPractice return b } func (b *ControlBuilder) WithMaturityLevel(maturityLevel string) *ControlBuilder { b.attrs["maturityLevel"] = maturityLevel return b } func (b *ControlBuilder) WithNotImplementedJustification(justification string) *ControlBuilder { b.attrs["notImplementedJustification"] = justification return b } func (b *ControlBuilder) Create() string { return CreateControl(b.client, b.frameworkID, b.attrs) } type MeasureBuilder struct { client *testutil.Client attrs Attrs } func NewMeasure(c *testutil.Client) *MeasureBuilder { return &MeasureBuilder{client: c, attrs: Attrs{}} } func (b *MeasureBuilder) WithName(name string) *MeasureBuilder { b.attrs["name"] = name return b } func (b *MeasureBuilder) WithDescription(desc string) *MeasureBuilder { b.attrs["description"] = desc return b } func (b *MeasureBuilder) WithCategory(category string) *MeasureBuilder { b.attrs["category"] = category return b } func (b *MeasureBuilder) Create() string { return CreateMeasure(b.client, b.attrs) } type TaskBuilder struct { client *testutil.Client measureID *string attrs Attrs } func NewTask(c *testutil.Client, measureID string) *TaskBuilder { return &TaskBuilder{client: c, measureID: &measureID, attrs: Attrs{}} } func NewTaskWithoutMeasure(c *testutil.Client) *TaskBuilder { return &TaskBuilder{client: c, measureID: nil, attrs: Attrs{}} } func (b *TaskBuilder) WithName(name string) *TaskBuilder { b.attrs["name"] = name return b } func (b *TaskBuilder) WithDescription(desc string) *TaskBuilder { b.attrs["description"] = desc return b } func (b *TaskBuilder) Create() string { return CreateTask(b.client, b.measureID, b.attrs) } type RiskBuilder struct { client *testutil.Client attrs Attrs } func NewRisk(c *testutil.Client) *RiskBuilder { return &RiskBuilder{client: c, attrs: Attrs{}} } func (b *RiskBuilder) WithName(name string) *RiskBuilder { b.attrs["name"] = name return b } func (b *RiskBuilder) WithDescription(desc string) *RiskBuilder { b.attrs["description"] = desc return b } func (b *RiskBuilder) WithCategory(category string) *RiskBuilder { b.attrs["category"] = category return b } func (b *RiskBuilder) WithTreatment(treatment string) *RiskBuilder { b.attrs["treatment"] = treatment return b } func (b *RiskBuilder) WithLikelihood(likelihood int) *RiskBuilder { b.attrs["inherentLikelihood"] = likelihood return b } func (b *RiskBuilder) WithImpact(impact int) *RiskBuilder { b.attrs["inherentImpact"] = impact return b } func (b *RiskBuilder) Create() string { return CreateRisk(b.client, b.attrs) } func CreateAudit(c *testutil.Client, frameworkID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateAuditInput!) { createAudit(input: $input) { auditEdge { node { id } } } } ` input := map[string]any{ "organizationId": c.GetOrganizationID().String(), "frameworkId": frameworkID, "name": a.getString("name", SafeName("Audit")), } if state := a.getStringPtr("state"); state != nil { input["state"] = *state } var result struct { CreateAudit struct { AuditEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"auditEdge"` } `json:"createAudit"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createAudit mutation failed") return result.CreateAudit.AuditEdge.Node.ID } type AuditBuilder struct { client *testutil.Client frameworkID string attrs Attrs } func NewAudit(c *testutil.Client, frameworkID string) *AuditBuilder { return &AuditBuilder{client: c, frameworkID: frameworkID, attrs: Attrs{}} } func (b *AuditBuilder) WithName(name string) *AuditBuilder { b.attrs["name"] = name return b } func (b *AuditBuilder) WithState(state string) *AuditBuilder { b.attrs["state"] = state return b } func (b *AuditBuilder) Create() string { return CreateAudit(b.client, b.frameworkID, b.attrs) } func CreateDatum(c *testutil.Client, ownerID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateDatumInput!) { createDatum(input: $input) { datumEdge { node { id } } } } ` input := map[string]any{ "organizationId": c.GetOrganizationID().String(), "ownerId": ownerID, "name": a.getString("name", SafeName("Datum")), "dataClassification": a.getString("dataClassification", "INTERNAL"), } if desc := a.getStringPtr("description"); desc != nil { input["description"] = *desc } var result struct { CreateDatum struct { DatumEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"datumEdge"` } `json:"createDatum"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createDatum mutation failed") return result.CreateDatum.DatumEdge.Node.ID } type DatumBuilder struct { client *testutil.Client ownerID string attrs Attrs } func NewDatum(c *testutil.Client, ownerID string) *DatumBuilder { return &DatumBuilder{client: c, ownerID: ownerID, attrs: Attrs{}} } func (b *DatumBuilder) WithName(name string) *DatumBuilder { b.attrs["name"] = name return b } func (b *DatumBuilder) WithDescription(desc string) *DatumBuilder { b.attrs["description"] = desc return b } func (b *DatumBuilder) WithDataClassification(classification string) *DatumBuilder { b.attrs["dataClassification"] = classification return b } func (b *DatumBuilder) Create() string { return CreateDatum(b.client, b.ownerID, b.attrs) } type DocumentBuilder struct { client *testutil.Client attrs Attrs versionID string } func NewDocument(c *testutil.Client) *DocumentBuilder { return &DocumentBuilder{client: c, attrs: Attrs{}} } func (b *DocumentBuilder) WithTitle(title string) *DocumentBuilder { b.attrs["title"] = title return b } func (b *DocumentBuilder) WithContent(content string) *DocumentBuilder { b.attrs["content"] = content return b } func (b *DocumentBuilder) WithDocumentType(docType string) *DocumentBuilder { b.attrs["documentType"] = docType return b } func (b *DocumentBuilder) WithClassification(classification string) *DocumentBuilder { b.attrs["classification"] = classification return b } func (b *DocumentBuilder) VersionID() string { return b.versionID } func (b *DocumentBuilder) Create() string { b.client.T.Helper() a := b.attrs const query = ` mutation($input: CreateDocumentInput!) { createDocument(input: $input) { documentEdge { node { id } } documentVersionEdge { node { id } } } } ` input := map[string]any{ "organizationId": b.client.GetOrganizationID().String(), "title": a.getString("title", SafeName("Document")), "documentType": a.getString("documentType", "POLICY"), "classification": a.getString("classification", "INTERNAL"), } var result struct { CreateDocument struct { DocumentEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"documentEdge"` DocumentVersionEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"documentVersionEdge"` } `json:"createDocument"` } err := b.client.Execute(query, map[string]any{"input": input}, &result) require.NoError(b.client.T, err, "createDocument mutation failed") b.versionID = result.CreateDocument.DocumentVersionEdge.Node.ID return result.CreateDocument.DocumentEdge.Node.ID } func CreateProcessingActivity(c *testutil.Client, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateProcessingActivityInput!) { createProcessingActivity(input: $input) { processingActivityEdge { node { id } } } } ` input := map[string]any{ "organizationId": c.GetOrganizationID().String(), "name": a.getString("name", SafeName("ProcessingActivity")), "specialOrCriminalData": a.getString("specialOrCriminalData", "NO"), "lawfulBasis": a.getString("lawfulBasis", "CONSENT"), "internationalTransfers": a.getBool("internationalTransfers", false), "dataProtectionImpactAssessmentNeeded": a.getString("dataProtectionImpactAssessmentNeeded", "NOT_NEEDED"), "transferImpactAssessmentNeeded": a.getString("transferImpactAssessmentNeeded", "NOT_NEEDED"), "role": a.getString("role", "CONTROLLER"), } if purpose := a.getStringPtr("purpose"); purpose != nil { input["purpose"] = *purpose } var result struct { CreateProcessingActivity struct { ProcessingActivityEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"processingActivityEdge"` } `json:"createProcessingActivity"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createProcessingActivity mutation failed") return result.CreateProcessingActivity.ProcessingActivityEdge.Node.ID } type ProcessingActivityBuilder struct { client *testutil.Client attrs Attrs } func NewProcessingActivity(c *testutil.Client) *ProcessingActivityBuilder { return &ProcessingActivityBuilder{client: c, attrs: Attrs{}} } func (b *ProcessingActivityBuilder) WithName(name string) *ProcessingActivityBuilder { b.attrs["name"] = name return b } func (b *ProcessingActivityBuilder) WithPurpose(purpose string) *ProcessingActivityBuilder { b.attrs["purpose"] = purpose return b } func (b *ProcessingActivityBuilder) WithLawfulBasis(basis string) *ProcessingActivityBuilder { b.attrs["lawfulBasis"] = basis return b } func (b *ProcessingActivityBuilder) WithInternationalTransfers(transfers bool) *ProcessingActivityBuilder { b.attrs["internationalTransfers"] = transfers return b } func (b *ProcessingActivityBuilder) WithSpecialOrCriminalData(value string) *ProcessingActivityBuilder { b.attrs["specialOrCriminalData"] = value return b } func (b *ProcessingActivityBuilder) Create() string { return CreateProcessingActivity(b.client, b.attrs) } func CreateAccessSource(c *testutil.Client, organizationID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateAccessSourceInput!) { createAccessSource(input: $input) { accessSourceEdge { node { id } } } } ` input := map[string]any{ "organizationId": organizationID, "name": a.getString("name", SafeName("AccessSource")), } if csvData := a.getStringPtr("csvData"); csvData != nil { input["csvData"] = *csvData } if connectorID := a.getStringPtr("connectorId"); connectorID != nil { input["connectorId"] = *connectorID } var result struct { CreateAccessSource struct { AccessSourceEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"accessSourceEdge"` } `json:"createAccessSource"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createAccessSource mutation failed") return result.CreateAccessSource.AccessSourceEdge.Node.ID } type AccessSourceBuilder struct { client *testutil.Client organizationID string attrs Attrs } func NewAccessSource(c *testutil.Client, organizationID string) *AccessSourceBuilder { return &AccessSourceBuilder{client: c, organizationID: organizationID, attrs: Attrs{}} } func (b *AccessSourceBuilder) WithName(name string) *AccessSourceBuilder { b.attrs["name"] = name return b } func (b *AccessSourceBuilder) WithCsvData(csvData string) *AccessSourceBuilder { b.attrs["csvData"] = csvData return b } func (b *AccessSourceBuilder) Create() string { return CreateAccessSource(b.client, b.organizationID, b.attrs) } func CreateAccessReviewCampaign(c *testutil.Client, organizationID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateAccessReviewCampaignInput!) { createAccessReviewCampaign(input: $input) { accessReviewCampaignEdge { node { id } } } } ` input := map[string]any{ "organizationId": organizationID, "name": a.getString("name", SafeName("Campaign")), } if v, ok := a["accessSourceIds"]; ok { input["accessSourceIds"] = v } var result struct { CreateAccessReviewCampaign struct { AccessReviewCampaignEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"accessReviewCampaignEdge"` } `json:"createAccessReviewCampaign"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createAccessReviewCampaign mutation failed") return result.CreateAccessReviewCampaign.AccessReviewCampaignEdge.Node.ID } type AccessReviewCampaignBuilder struct { client *testutil.Client organizationID string attrs Attrs } func NewAccessReviewCampaign(c *testutil.Client, organizationID string) *AccessReviewCampaignBuilder { return &AccessReviewCampaignBuilder{client: c, organizationID: organizationID, attrs: Attrs{}} } func (b *AccessReviewCampaignBuilder) WithName(name string) *AccessReviewCampaignBuilder { b.attrs["name"] = name return b } func (b *AccessReviewCampaignBuilder) WithAccessSourceIDs(ids []string) *AccessReviewCampaignBuilder { b.attrs["accessSourceIds"] = ids return b } func (b *AccessReviewCampaignBuilder) Create() string { return CreateAccessReviewCampaign(b.client, b.organizationID, b.attrs) } type StatementOfApplicabilityBuilder struct { client *testutil.Client attrs Attrs } func NewStatementOfApplicability(c *testutil.Client) *StatementOfApplicabilityBuilder { return &StatementOfApplicabilityBuilder{client: c, attrs: Attrs{}} } func (b *StatementOfApplicabilityBuilder) WithName(name string) *StatementOfApplicabilityBuilder { b.attrs["name"] = name return b } func (b *StatementOfApplicabilityBuilder) Create() string { b.client.T.Helper() a := b.attrs const query = ` mutation($input: CreateStatementOfApplicabilityInput!) { createStatementOfApplicability(input: $input) { statementOfApplicabilityEdge { node { id } } } } ` input := map[string]any{ "organizationId": b.client.GetOrganizationID().String(), "name": a.getString("name", SafeName("SOA")), } var result struct { CreateStatementOfApplicability struct { StatementOfApplicabilityEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"statementOfApplicabilityEdge"` } `json:"createStatementOfApplicability"` } err := b.client.Execute(query, map[string]any{"input": input}, &result) require.NoError(b.client.T, err, "createStatementOfApplicability mutation failed") return result.CreateStatementOfApplicability.StatementOfApplicabilityEdge.Node.ID } func CreateApplicabilityStatement(c *testutil.Client, soaID, controlID string, applicability bool, justification *string) string { c.T.Helper() const query = ` mutation($input: CreateApplicabilityStatementInput!) { createApplicabilityStatement(input: $input) { applicabilityStatementEdge { node { id } } } } ` input := map[string]any{ "statementOfApplicabilityId": soaID, "controlId": controlID, "applicability": applicability, } if justification != nil { input["justification"] = *justification } var result struct { CreateApplicabilityStatement struct { ApplicabilityStatementEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"applicabilityStatementEdge"` } `json:"createApplicabilityStatement"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createApplicabilityStatement mutation failed") return result.CreateApplicabilityStatement.ApplicabilityStatementEdge.Node.ID } type OAuth2ClientResult struct { ClientID string ClientSecret string } func CreateOAuth2Client(c *testutil.Client, attrs Attrs) OAuth2ClientResult { input := map[string]any{ "organization_id": c.GetOrganizationID().String(), "client_name": SafeName("OAuth2 Client"), "visibility": "private", "redirect_uris": []string{"http://localhost:9999/callback"}, "grant_types": []string{ "authorization_code", "refresh_token", }, "response_types": []string{"code"}, "token_endpoint_auth_method": "client_secret_basic", "scopes": "openid email profile offline_access", } maps.Copy(input, attrs) resp, raw, err := testutil.OAuth2RegisterClient(c, input) require.NoError(c.T, err, "OAuth2 client registration failed") require.NotNil(c.T, resp, "OAuth2 client registration returned nil (status=%d body=%s)", raw.StatusCode, string(raw.Body)) return OAuth2ClientResult{ ClientID: resp.ClientID, ClientSecret: resp.ClientSecret, } } func CreatePublicOAuth2Client(c *testutil.Client, attrs Attrs) OAuth2ClientResult { input := map[string]any{ "organization_id": c.GetOrganizationID().String(), "client_name": SafeName("Public OAuth2 Client"), "visibility": "private", "redirect_uris": []string{"http://localhost:9999/callback"}, "grant_types": []string{ "authorization_code", "refresh_token", "urn:ietf:params:oauth:grant-type:device_code", }, "response_types": []string{"code"}, "token_endpoint_auth_method": "none", "scopes": "openid email profile offline_access", } maps.Copy(input, attrs) resp, raw, err := testutil.OAuth2RegisterClient(c, input) require.NoError(c.T, err, "public OAuth2 client registration failed") require.NotNil(c.T, resp, "public OAuth2 client registration returned nil (status=%d body=%s)", raw.StatusCode, string(raw.Body)) return OAuth2ClientResult{ ClientID: resp.ClientID, ClientSecret: resp.ClientSecret, } } func SafeOrigin() string { return fmt.Sprintf("https://%s.example.com", strings.ToLower(gofakeit.LetterN(10))) } func CreateCookieBanner(c *testutil.Client, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateCookieBannerInput!) { createCookieBanner(input: $input) { cookieBannerEdge { node { id } } } } ` input := map[string]any{ "organizationId": c.GetOrganizationID().String(), "name": a.getString("name", SafeName("CookieBanner")), "origin": a.getString("origin", SafeOrigin()), "cookiePolicyUrl": a.getString("cookiePolicyUrl", "https://example.com/cookies"), "consentExpiryDays": a.getInt("consentExpiryDays", 365), } if ppURL := a.getStringPtr("privacyPolicyUrl"); ppURL != nil { input["privacyPolicyUrl"] = *ppURL } var result struct { CreateCookieBanner struct { CookieBannerEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"cookieBannerEdge"` } `json:"createCookieBanner"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createCookieBanner mutation failed") return result.CreateCookieBanner.CookieBannerEdge.Node.ID } type CookieBannerBuilder struct { client *testutil.Client attrs Attrs } func NewCookieBanner(c *testutil.Client) *CookieBannerBuilder { return &CookieBannerBuilder{client: c, attrs: Attrs{}} } func (b *CookieBannerBuilder) WithName(name string) *CookieBannerBuilder { b.attrs["name"] = name return b } func (b *CookieBannerBuilder) WithOrigin(origin string) *CookieBannerBuilder { b.attrs["origin"] = origin return b } func (b *CookieBannerBuilder) WithCookiePolicyUrl(url string) *CookieBannerBuilder { b.attrs["cookiePolicyUrl"] = url return b } func (b *CookieBannerBuilder) WithPrivacyPolicyUrl(url string) *CookieBannerBuilder { b.attrs["privacyPolicyUrl"] = url return b } func (b *CookieBannerBuilder) WithConsentExpiryDays(days int) *CookieBannerBuilder { b.attrs["consentExpiryDays"] = days return b } func (b *CookieBannerBuilder) Create() string { return CreateCookieBanner(b.client, b.attrs) } func CreateCookieCategory(c *testutil.Client, bannerID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateCookieCategoryInput!) { createCookieCategory(input: $input) { cookieCategoryEdge { node { id } } } } ` input := map[string]any{ "cookieBannerId": bannerID, "name": a.getString("name", SafeName("Category")), "slug": a.getString("slug", strings.ToLower(gofakeit.LetterN(8))), "description": a.getString("description", "Test cookie category"), "rank": a.getInt("rank", 10), } var result struct { CreateCookieCategory struct { CookieCategoryEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"cookieCategoryEdge"` } `json:"createCookieCategory"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createCookieCategory mutation failed") return result.CreateCookieCategory.CookieCategoryEdge.Node.ID } func CreateTrackerPattern(c *testutil.Client, categoryID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateTrackerPatternInput!) { createTrackerPattern(input: $input) { trackerPatternEdge { node { id } } } } ` input := map[string]any{ "cookieCategoryId": categoryID, "trackerType": a.getString("trackerType", "COOKIE"), "pattern": a.getString("pattern", gofakeit.LetterN(8)+"_cookie"), "matchType": a.getString("matchType", "EXACT"), "displayName": a.getString("displayName", SafeName("Pattern")), "description": a.getString("description", "Test tracker pattern"), } if _, ok := a["maxAgeSeconds"]; ok { input["maxAgeSeconds"] = a.getInt("maxAgeSeconds", 0) } var result struct { CreateTrackerPattern struct { TrackerPatternEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"trackerPatternEdge"` } `json:"createTrackerPattern"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createTrackerPattern mutation failed") return result.CreateTrackerPattern.TrackerPatternEdge.Node.ID } func CreateTrackerResource(c *testutil.Client, categoryID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateTrackerResourceInput!) { createTrackerResource(input: $input) { trackerResourceEdge { node { id } } } } ` input := map[string]any{ "cookieCategoryId": categoryID, "type": a.getString("type", "SCRIPT"), "origin": a.getString("origin", SafeOrigin()), "path": a.getString("path", "/"+gofakeit.LetterN(8)+".js"), "displayName": a.getString("displayName", SafeName("Resource")), "description": a.getString("description", "Test tracker resource"), } var result struct { CreateTrackerResource struct { TrackerResourceEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"trackerResourceEdge"` } `json:"createTrackerResource"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createTrackerResource mutation failed") return result.CreateTrackerResource.TrackerResourceEdge.Node.ID } func ReportDetectedResources(c *testutil.Client, bannerID string, count int) { c.T.Helper() resources := make([]map[string]string, 0, count) for range count { resources = append(resources, map[string]string{ "url": fmt.Sprintf("https://%s.example.com/%s.js", strings.ToLower(gofakeit.LetterN(8)), gofakeit.LetterN(6)), "resource_type": "script", }) } body, err := json.Marshal(map[string]any{"resources": resources}) require.NoError(c.T, err, "cannot marshal report body") url := fmt.Sprintf("%s/api/cookie-banner/v1/%s/report", c.BaseURL(), bannerID) resp, err := http.Post(url, "application/json", bytes.NewReader(body)) require.NoError(c.T, err, "report detected resources request failed") defer func() { _ = resp.Body.Close() }() require.Equal(c.T, http.StatusNoContent, resp.StatusCode, "report detected resources unexpected status") } func CreateRiskAssessment(c *testutil.Client, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateRiskAssessmentInput!) { createRiskAssessment(input: $input) { riskAssessmentEdge { node { id } } } } ` input := map[string]any{ "organizationId": c.GetOrganizationID().String(), "name": a.getString("name", SafeName("Risk Assessment")), } if desc := a.getStringPtr("description"); desc != nil { input["description"] = *desc } var result struct { CreateRiskAssessment struct { RiskAssessmentEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"riskAssessmentEdge"` } `json:"createRiskAssessment"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessment mutation failed") return result.CreateRiskAssessment.RiskAssessmentEdge.Node.ID } func CreateRiskAssessmentScope(c *testutil.Client, riskAssessmentID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateRiskAssessmentScopeInput!) { createRiskAssessmentScope(input: $input) { riskAssessmentScopeEdge { node { id } } } } ` input := map[string]any{ "riskAssessmentId": riskAssessmentID, "name": a.getString("name", SafeName("Scope")), } var result struct { CreateRiskAssessmentScope struct { RiskAssessmentScopeEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"riskAssessmentScopeEdge"` } `json:"createRiskAssessmentScope"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessmentScope mutation failed") return result.CreateRiskAssessmentScope.RiskAssessmentScopeEdge.Node.ID } func CreateRiskAssessmentNode(c *testutil.Client, scopeID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateRiskAssessmentNodeInput!) { createRiskAssessmentNode(input: $input) { riskAssessmentNodeEdge { node { id } } } } ` input := map[string]any{ "riskAssessmentScopeId": scopeID, "nodeType": a.getString("nodeType", "ASSET"), "name": a.getString("name", SafeName("Node")), } var result struct { CreateRiskAssessmentNode struct { RiskAssessmentNodeEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"riskAssessmentNodeEdge"` } `json:"createRiskAssessmentNode"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessmentNode mutation failed") return result.CreateRiskAssessmentNode.RiskAssessmentNodeEdge.Node.ID } func CreateRiskAssessmentProcess(c *testutil.Client, scopeID, sourceNodeID, targetNodeID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateRiskAssessmentProcessInput!) { createRiskAssessmentProcess(input: $input) { riskAssessmentProcessEdge { node { id } } } } ` input := map[string]any{ "riskAssessmentScopeId": scopeID, "sourceNodeId": sourceNodeID, "targetNodeId": targetNodeID, "name": a.getString("name", SafeName("Process")), } var result struct { CreateRiskAssessmentProcess struct { RiskAssessmentProcessEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"riskAssessmentProcessEdge"` } `json:"createRiskAssessmentProcess"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessmentProcess mutation failed") return result.CreateRiskAssessmentProcess.RiskAssessmentProcessEdge.Node.ID } func CreateRiskAssessmentThreat(c *testutil.Client, scopeID, processID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateRiskAssessmentThreatInput!) { createRiskAssessmentThreat(input: $input) { riskAssessmentThreatEdge { node { id } } } } ` input := map[string]any{ "riskAssessmentScopeId": scopeID, "processId": processID, "name": a.getString("name", SafeName("Threat")), "category": a.getString("category", "Confidentiality"), } var result struct { CreateRiskAssessmentThreat struct { RiskAssessmentThreatEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"riskAssessmentThreatEdge"` } `json:"createRiskAssessmentThreat"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessmentThreat mutation failed") return result.CreateRiskAssessmentThreat.RiskAssessmentThreatEdge.Node.ID } func CreateRiskAssessmentScenario(c *testutil.Client, scopeID string, attrs ...Attrs) string { c.T.Helper() var a Attrs if len(attrs) > 0 { a = attrs[0] } const query = ` mutation($input: CreateRiskAssessmentScenarioInput!) { createRiskAssessmentScenario(input: $input) { riskAssessmentScenarioEdge { node { id } } } } ` input := map[string]any{ "riskAssessmentScopeId": scopeID, "name": a.getString("name", SafeName("Scenario")), } if desc := a.getStringPtr("description"); desc != nil { input["description"] = *desc } var result struct { CreateRiskAssessmentScenario struct { RiskAssessmentScenarioEdge struct { Node struct { ID string `json:"id"` } `json:"node"` } `json:"riskAssessmentScenarioEdge"` } `json:"createRiskAssessmentScenario"` } err := c.Execute(query, map[string]any{"input": input}, &result) require.NoError(c.T, err, "createRiskAssessmentScenario mutation failed") return result.CreateRiskAssessmentScenario.RiskAssessmentScenarioEdge.Node.ID } func LinkRiskAssessmentScenarioThreat(c *testutil.Client, scenarioID, threatID string) { c.T.Helper() const query = ` mutation($input: LinkRiskAssessmentScenarioThreatInput!) { linkRiskAssessmentScenarioThreat(input: $input) { riskAssessmentScenario { id } } } ` _, err := c.Do(query, map[string]any{ "input": map[string]any{ "riskAssessmentScenarioId": scenarioID, "threatId": threatID, }, }) require.NoError(c.T, err, "linkRiskAssessmentScenarioThreat mutation failed") } func LinkRiskAssessmentScenarioRisk(c *testutil.Client, scenarioID, riskID string) { c.T.Helper() const query = ` mutation($input: LinkRiskAssessmentScenarioRiskInput!) { linkRiskAssessmentScenarioRisk(input: $input) { riskAssessmentScenario { id } } } ` _, err := c.Do(query, map[string]any{ "input": map[string]any{ "riskAssessmentScenarioId": scenarioID, "riskId": riskID, }, }) require.NoError(c.T, err, "linkRiskAssessmentScenarioRisk mutation failed") }