Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/api/src/common/test/test-transaction-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { DataSource, EntityManager } from "typeorm"
import { Agent } from "@/domains/agents/agent.entity"
import { ConversationAgentSession } from "@/domains/agents/conversation-agent-sessions/conversation-agent-session.entity"
import { ExtractionAgentSession } from "@/domains/agents/extraction-agent-sessions/extraction-agent-session.entity"
import { FormAgentSession } from "@/domains/agents/form-agent-sessions/form-agent-session.entity"
import { AgentMembership } from "@/domains/agents/memberships/agent-membership.entity"
import { AgentMessage } from "@/domains/agents/shared/agent-session-messages/agent-message.entity"
import { AgentMessageFeedback } from "@/domains/agents/shared/agent-session-messages/feedback/agent-message-feedback.entity"
Expand Down Expand Up @@ -42,6 +43,7 @@ export interface TransactionalTestSetup {
agentMembershipRepository: Repository<AgentMembership>
extractionAgentSessionRepository: Repository<ExtractionAgentSession>
conversationAgentSessionRepository: Repository<ConversationAgentSession>
formAgentSessionRepository: Repository<FormAgentSession>
documentRepository: Repository<Document>
evaluationReportRepository: Repository<EvaluationReport>
evaluationRepository: Repository<Evaluation>
Expand Down Expand Up @@ -207,6 +209,7 @@ export async function setupTransactionalTestDatabase(
agentRepository: getRepository(Agent),
extractionAgentSessionRepository: getRepository(ExtractionAgentSession),
conversationAgentSessionRepository: getRepository(ConversationAgentSession),
formAgentSessionRepository: getRepository(FormAgentSession),
agentMessageRepository: getRepository(AgentMessage),
agentMessageFeedbackRepository: getRepository(AgentMessageFeedback),
documentRepository: getRepository(Document),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,7 @@ describe("ExtractionAgentSessions - Auth", () => {
})
})

// FIXME: it works with UI but fails in tests
describe.skip("ExtractionAgentSessionsRoutes.getOne", () => {
describe("ExtractionAgentSessionsRoutes.getOne", () => {
const subject = async (type: "playground" | "live") =>
request({
route: ExtractionAgentSessionsRoutes.getOne,
Expand Down Expand Up @@ -208,8 +207,7 @@ describe("ExtractionAgentSessions - Auth", () => {
})
})

// FIXME: it works with UI but fails in tests
describe.skip("ConversationAgentSessionsRoutes.deleteOne", () => {
describe("ExtractionAgentSessionsRoutes.deleteOne", () => {
const subject = async (type: "playground" | "live") =>
request({
route: ExtractionAgentSessionsRoutes.deleteOne,
Expand Down
223 changes: 223 additions & 0 deletions apps/api/src/domains/agents/form-agent-sessions/e2e-tests/auth.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import { randomUUID } from "node:crypto"
import {
FormAgentSessionsRoutes,
type ProjectMembershipRoleDto,
} from "@caseai-connect/api-contracts"
import { afterAll } from "@jest/globals"
import type { INestApplication } from "@nestjs/common"
import type { App } from "supertest/types"
import { AUTH_ERRORS } from "@/common/errors/auth-errors"
import { clearTestDatabase } from "@/common/test/test-database"
import {
type AllRepositories,
setupTransactionalTestDatabase,
teardownTestDatabase,
} from "@/common/test/test-transaction-manager"
import { removeNullish } from "@/common/utils/remove-nullish"
import { createOrganizationWithAgentSession } from "@/domains/organizations/organization.factory"
import { sdk } from "@/external/llm/open-telemetry-init"
import { setupUserGuardForTesting } from "../../../../../test/e2e.helpers"
import { expectResponse, type Requester, testRequester } from "../../../../../test/request"
import { FormAgentSessionsModule } from "../form-agent-sessions.module"

describe("Agent Sessions - Auth", () => {
let app: INestApplication<App>
let request: Requester
let setup: Awaited<ReturnType<typeof setupTransactionalTestDatabase>>
let repositories: AllRepositories

// Variables for the tests
let organizationId: string | null = randomUUID()
let projectId: string | null = randomUUID()
let agentId: string | null = randomUUID()
let agentSessionId: string | null = randomUUID()
let accessToken: string | null = "token"
let auth0Id = "auth0|123"

beforeAll(async () => {
setup = await setupTransactionalTestDatabase({
additionalImports: [FormAgentSessionsModule],
applyOverrides: (moduleBuilder) => setupUserGuardForTesting(moduleBuilder, () => auth0Id),
})
repositories = setup.getAllRepositories()

app = setup.module.createNestApplication()
await app.init()
request = testRequester(app)
})

beforeEach(async () => {
await clearTestDatabase(setup.dataSource)
organizationId = randomUUID()
projectId = randomUUID()
agentId = randomUUID()
agentSessionId = randomUUID()
accessToken = "token"
auth0Id = "auth0|123"
})

afterAll(async () => {
await teardownTestDatabase(setup)
await sdk.shutdown()
await app.close()
})

const createContextForRole = async (role: ProjectMembershipRoleDto) => {
const { user, organization, project, agent, agentSession } =
await createOrganizationWithAgentSession({
repositories,
params: {
projectMembership: { role },
},
agentType: "form",
})
organizationId = organization.id
projectId = project.id
agentId = agent.id
agentSessionId = agentSession.id
accessToken = "token"
auth0Id = user.auth0Id
}

describe("FormAgentSessionsRoutes.createOne", () => {
const subject = async (type: "playground" | "live") =>
request({
route: FormAgentSessionsRoutes.createOne,
pathParams: removeNullish({ organizationId, projectId, agentId }),
token: accessToken ?? undefined,
request: { payload: { type } },
})

describe.each([["live"], ["playground"]] as const)("creating a %s session", (type) => {
it("requires an authentication token", async () => {
accessToken = null
expectResponse(await subject(type), 401, AUTH_ERRORS.NO_ACCESS_TOKEN)
})

it("requires a valid organization ID", async () => {
organizationId = null
expectResponse(await subject(type), 400, AUTH_ERRORS.NO_ORGANIZATION_ID)
})
it("requires a valid agent ID", async () => {
await createContextForRole("member")
agentId = null
expectResponse(await subject(type), 404)
})

it("requires the user to be a member of the organization", async () => {
await createContextForRole("member")
auth0Id = "another-auth0-id"
expectResponse(await subject(type), 401, AUTH_ERRORS.NOT_MEMBER_OF_ORG)
})

if (type === "playground") {
it("doesn't allow members to create playground sessions", async () => {
await createContextForRole("member")
expectResponse(await subject(type), 403, AUTH_ERRORS.UNAUTHORIZED_RESOURCE)
})
} else {
it("allows members to create live sessions", async () => {
await createContextForRole("member")
expectResponse(await subject(type), 201)
})
}

it("allows owners to create live sessions", async () => {
await createContextForRole("owner")
expectResponse(await subject(type), 201)
})
})
})

describe("FormAgentSessionsRoutes.getAll", () => {
const subject = async (type: "playground" | "live") =>
request({
route: FormAgentSessionsRoutes.getAll,
pathParams: removeNullish({ organizationId, projectId, agentId }),
token: accessToken ?? undefined,
request: { payload: { type } },
})

describe.each([["live"], ["playground"]] as const)("getting %s sessions", (type) => {
it("requires an authentication token", async () => {
accessToken = null
expectResponse(await subject(type), 401, AUTH_ERRORS.NO_ACCESS_TOKEN)
})
it("requires a valid organization ID", async () => {
organizationId = null
expectResponse(await subject(type), 400, AUTH_ERRORS.NO_ORGANIZATION_ID)
})
it("requires a valid agent ID", async () => {
await createContextForRole("owner")
agentId = null
expectResponse(await subject(type), 404)
})
if (type === "playground") {
it("doesn't allow simple member to get playground sessions", async () => {
await createContextForRole("member")
expectResponse(await subject(type), 403, AUTH_ERRORS.UNAUTHORIZED_RESOURCE)
})
} else {
it("allows members to get live sessions", async () => {
await createContextForRole("member")
expectResponse(await subject(type), 201)
})
}
it("allows owner to get sessions", async () => {
await createContextForRole("owner")
expectResponse(await subject(type), 201)
})
it("requires the user to be a member of the organization", async () => {
await createContextForRole("owner")
auth0Id = "another-auth0-id"
expectResponse(await subject(type), 401, AUTH_ERRORS.NOT_MEMBER_OF_ORG)
})
})
})

describe("FormAgentSessionsRoutes.deleteOne", () => {
const subject = async (type: "playground" | "live") =>
request({
route: FormAgentSessionsRoutes.deleteOne,
pathParams: removeNullish({ organizationId, projectId, agentId, agentSessionId }),
token: accessToken ?? undefined,
request: { payload: { type } },
})

describe.each([["live"], ["playground"]] as const)("deleting a %s session", (type) => {
it("requires an authentication token", async () => {
accessToken = null
expectResponse(await subject(type), 401, AUTH_ERRORS.NO_ACCESS_TOKEN)
})
it("requires a valid organization ID", async () => {
organizationId = null
expectResponse(await subject(type), 400, AUTH_ERRORS.NO_ORGANIZATION_ID)
})
it("requires a valid agent ID", async () => {
await createContextForRole("owner")
agentId = null
expectResponse(await subject(type), 404)
})
it("requires the user to be a member of the organization", async () => {
await createContextForRole("owner")
auth0Id = "another-auth0-id"
expectResponse(await subject(type), 401, AUTH_ERRORS.NOT_MEMBER_OF_ORG)
})
if (type === "playground") {
it("doesn't allow a simple member to delete playground sessions", async () => {
await createContextForRole("member")
expectResponse(await subject(type), 403, AUTH_ERRORS.UNAUTHORIZED_RESOURCE)
})
} else {
it("allows member to delete sessions", async () => {
await createContextForRole("member")
expectResponse(await subject(type), 201)
})
}
it("allows owner to delete sessions", async () => {
await createContextForRole("owner")
expectResponse(await subject(type), 201)
})
})
})
})
Loading
Loading