import { randomUUID } from "crypto"; import { graphql } from "../../src/graphql"; import { resolveTailorAuth } from "./auth-machine-user"; const UPDATE_ROLE_MUTATION = graphql(` mutation E2eUpdateRole( $roleId: ID! $name: String $description: String $permissions: [String!]! ) { updateRole(roleId: $roleId, name: $name, description: $description, permissions: $permissions) { roleId } } `); const CREATE_ROLE_MUTATION = graphql(` mutation E2eCreateRole($name: String!, $description: String, $permissions: [String!]!) { createRole(name: $name, description: $description, permissions: $permissions) { id name description } } `); interface RoleFactoryInput { name?: string; description?: string; permissions?: string[]; } function buildRoleInput(overrides: RoleFactoryInput = {}) { const id = randomUUID().slice(0, 8); return { name: overrides.name ?? `e2e-role-${id}`, description: overrides.description ?? "", permissions: overrides.permissions ?? ["user-management:user"], }; } async function mutationWithRetry(fn: () => T | Promise, retries = 4): Promise> { let lastError: unknown; let backoff = 500; for (let attempt = 0; attempt < retries; attempt += 1) { try { return await fn(); } catch (error) { lastError = error; const message = error instanceof Error ? error.message : String(error); if (!/backend read error|network/i.test(message) || attempt === retries - 1) { throw error; } await new Promise((resolve) => setTimeout(resolve, backoff)); backoff = Math.min(backoff * 2, 4_000); } } throw lastError; } export async function createRole(inputOverrides: RoleFactoryInput = {}) { const { graphqlClient } = resolveTailorAuth(); const roleInput = buildRoleInput(inputOverrides); const result = await mutationWithRetry(() => graphqlClient.mutation(CREATE_ROLE_MUTATION, roleInput), ); if (result.error) { throw new Error(`Failed to create role: ${result.error.message}`); } return { roleId: result.data!.createRole.id, name: result.data!.createRole.name, description: result.data!.createRole.description, permissions: roleInput.permissions, }; } interface UpdateRoleInput { roleId: string; name?: string; description?: string; permissions: string[]; } export async function updateRole(input: UpdateRoleInput) { const { graphqlClient } = resolveTailorAuth(); const result = await mutationWithRetry(() => graphqlClient.mutation(UPDATE_ROLE_MUTATION, input)); if (result.error) { throw new Error(`Failed to update role: ${result.error.message}`); } return { roleId: result.data!.updateRole.roleId }; }