免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

SurfSense Composio Google Drive 连接器端到端测试全解析:从 OAuth 连接到文件索引再到聊天验证的完整旅程

SurfSense Composio Google Drive 连接器端到端测试全解析:从 OAuth 连接到文件索引再到聊天验证的完整旅程 SurfSense Composio Google Drive 连接器端到端测试全解析从 OAuth 连接到文件索引再到聊天验证的完整旅程【免费下载链接】SurfSenseOpen-source NotebookLM alternative. Research the open web with live data(Reddit, YT, IG, TikTok, Indeed, Google Search, Maps etc) through one platform, API or MCP server. Join our Discord: https://discord.gg/ejRNvftDp9项目地址: https://gitcode.com/GitHub_Trending/su/SurfSense本篇技术指南以 SurfSense 仓库中surfsense_web/tests/connectors/composio/drive/目录下的 E2E 测试文档为核心系统讲解 Composio Google Drive 连接器的端到端E2E测试体系一条journey.spec.ts如何串起「OAuth 连接 → 选择文件 → 触发索引 → 文档入库 → 聊天引用」的完整用户旅程金丝雀canary令牌如何证明索引管线真实生效以及哪些边界场景要交给 pytest、哪些通过并不代表真实集成可用。读完本文你将掌握 SurfSense 中 Playwright 严格 Fake SDK 分层测试的完整套路并能够据此为第四个 Composio toolkit如 Slack添加同类旅程测试。一、这份文档是什么Phase 1 的 Composio Drive 连接器覆盖在 SurfSense 的 E2E 测试体系中surfsense_web/tests/connectors/composio/drive/README.md是 Phase 1 Playwright 覆盖范围的说明文档。它的定位非常明确用浏览器层级的真实交互验证 Composio Google Drive 连接器用户可见的完整旅程而不是把每个后端细节都搬到 Playwright 里。该目录下唯一的核心用例是journey.spec.ts文档用一张表概括了它的用户预期文件用户预期journey.spec.ts我连接了 Google Drive选择一个文件等待索引完成然后 SurfSense 中就包含该文件的内容。从源码看这条用例确实完整走完了「OAuth fixture → 选择持久化 → 索引 → 存储的 source_markdown → 编辑器内容检索 → 聊天」的整条链路见 journey.spec.ts 的注释描述。二、核心旅程journey.spec.ts 一步一步做了什么journey.spec.ts是整篇文档的灵魂。它使用composioDriveWithChatTest这一组合 fixture其中composioDriveConnector负责在 fake 上完成一次 OAuth 连接并自动清理见 composio-drive.fixture.ts整个测试设置了240 秒超时test.setTimeout(240_000)注释里写明这是因为要覆盖「worker 冷启动 Docling 解析 摘要 嵌入 分块」全流程journey.spec.ts。2.1 打开页面并确认连接器入口用例先访问/dashboard/${workspace.id}/new-chat然后调用expectImportConnectorAvailable(page, Google Drive)断言「Google Drive」连接器入口在导入弹窗中可用。这验证了仪表盘与连接器对话框对已认证用户正常渲染。2.2 选择文件并持久化配置用例从FAKE_DRIVE_FILES中取两个文件定义在 canary.tsconst selectedFiles [ { id: FAKE_DRIVE_FILES.canary.id, // fake-file-canary → e2e-canary.txt name: FAKE_DRIVE_FILES.canary.name, mimeType: FAKE_DRIVE_FILES.canary.mimeType, }, { id: FAKE_DRIVE_FILES.pdfComposio.id, // fake-file-pdf-composio → e2e-composio-canary.pdf name: FAKE_DRIVE_FILES.pdfComposio.name, mimeType: FAKE_DRIVE_FILES.pdfComposio.mimeType, }, ]; const indexingOptions { max_files_per_folder: 10, incremental_sync: false, include_subfolders: false, }; await updateConnectorConfig(request, apiToken, composioDriveConnector.id, { ...composioDriveConnector.config, selected_folders: [], selected_files: selectedFiles, indexing_options: indexingOptions, });这里selected_folders、selected_files、indexing_options含max_files_per_folder、incremental_sync、include_subfolders都是连接器配置的核心字段。该配置通过 API 更新连接器后持久化——后端集成测试test_save_round_trips_selected_files会在 SQLAlchemy 会话中refresh(drive_connector)并断言drive_connector.config[selected_files] selected_files验证配置确实写入了数据库见 test_drive_folders_route.py。2.3 触发索引并等待完成await triggerIndex(request, apiToken, composioDriveConnector.id, workspace.id, { files: selectedFiles, indexing_options: indexingOptions, }); await waitForIndexingComplete(request, apiToken, composioDriveConnector.id, workspace.id, { timeoutMs: 240_000, intervalMs: 1_500, minDocuments: 2, });索引完成后用例分别用 30 秒和 60 秒的超时等待两个文档按标题出现e2e-canary.txt与e2e-composio-canary.pdf并从listDocuments结果中取出文档断言document_type GOOGLE_DRIVE_FILE——确认它们确实以 Drive 文件类型入库而不是别的文档类型。2.4 校验编辑器内容与分块const editor await getEditorContent(request, apiToken, workspace.id, canaryDoc.id); expect(editor.source_markdown).toContain(CANARY_TOKENS.driveCanaryFile); expect(editor.document_type).toBe(GOOGLE_DRIVE_FILE); expect(editor.chunk_count).toBeGreaterThan(0);代码注释点明了这里的分层设计content存放 LLM 摘要原始文件正文存放在source_markdown而getEditorContent正是 UI 打开文档时命中的同一端点journey.spec.ts。对 PDF 文件同样断言其source_markdown包含 PDF 金丝雀令牌SURFSENSE_E2E_CANARY_TOKEN_COMPOSIO_DRIVE_PDF_001且chunk_count 0。用例最后还刷新连接器列表断言last_indexed_at非空证明索引任务确实被记录到了连接器上。2.5 聊天验证索引结果可以被检索并引用const chat await streamChatToCompletion(request, apiToken, { workspaceId: workspace.id, threadId: chatThread.id, query: What is in my e2e-canary.txt Drive file?, }); expect(chat.assistantText).toContain(CANARY_TOKENS.driveCanaryFile);对 PDF 用同样方式询问 What is in my e2e-composio-canary.pdf Drive file?断言回复中包含 PDF 金丝雀令牌。这一步是整个旅程的收尾文件不仅被索引其内容还能被聊天 Agent 检索并回显证明从连接器到索引管线再到检索/对话的整条链路是通的。三、金丝雀机制如何证明索引真的发生了整个旅程依赖一个聪明的**金丝雀令牌canary token**设计。所有令牌集中在 canary.tsexport const CANARY_TOKENS { driveCanaryFile: SURFSENSE_E2E_CANARY_TOKEN_DRIVE_001, composioDrivePdfCanary: SURFSENSE_E2E_CANARY_TOKEN_COMPOSIO_DRIVE_PDF_001, // ... } as const;这些令牌的出身在后端 fake 的 fixture 数据中surfsense_backend/tests/e2e/fakes/fixtures/drive_files.json的_file_contents字段把固定字符串嵌入假文件内容例如fake-file-canary: Canary token for E2E tests: SURFSENSE_E2E_CANARY_TOKEN_DRIVE_001\nThis files content is asserted by indexing.spec.ts to confirm the indexing pipeline ran end-to-end.同时_file_binary_paths将两个 PDF 文件指向真实的二进制 fixturebinary/drive-canary.pdf、binary/composio-drive-canary.pdf对应仓库中surfsense_backend/tests/e2e/fakes/fixtures/binary/下的文件。由于令牌是按文件 id 固定的稳定字符串canary.ts多轮测试保持确定性且当断言失败时Document.content中的令牌可以直接在失败日志里被 grep 到。这正是文档中所说的核心断言目标Document.contentcontains the Drive canary token after indexing——一旦文档正文里出现了这个哨兵字符串就说明文件内容真正穿越了「下载 → 解析 → 摘要 → 嵌入 → 分块 → 入库」整条管线而非被 mock 出来的假象。四、passes到底证明了什么文档明确列出了这条旅程用例通过时所证明的五件事每一项都能在源码中找到对应断言仪表盘与连接器对话框对已认证用户正常渲染——由expectImportConnectorAvailable(page, Google Drive)覆盖。Composio Drive 连接器 fixture 能完成 OAuth happy path 设置——由runComposioOAuthcomposioDriveConnectorfixture 覆盖见 composio-drive.fixture.ts。所选 Drive 文件配置可以被持久化——由updateConnectorConfig 后端test_save_round_trips_selected_files共同佐证。管线服务能端到端地对已索引文件执行摘要 / 嵌入 / 分块——由chunk_count 0、source_markdown含令牌等断言覆盖。从 FastAPI 进程能触达 Celery worker队列 broker——索引任务经triggerIndex异步投递waitForIndexingComplete轮询到完成间接证明 broker/worker 链路正常。索引后Document.content包含 Drive 金丝雀令牌——由editor.source_markdown断言直接覆盖。五、Playwright 不负责的边界交给 pytest 更便宜文档特意强调Playwright 不拥有后端边界场景这些场景在 pytest 中更便宜、更容易定位。文档点名了三个后端测试文件逐一对照源码5.1 OAuth state 的新鲜度 / 篡改 / 畸形校验surfsense_backend/tests/unit/utils/test_oauth_security.py针对OAuthStateManager覆盖了四类场景接受新鲜的签名 statetest_validate_state_accepts_fresh_signed_state校验space_id、user_id、toolkit_id正确解码拒绝过期 statemax_age_seconds600时间戳超窗返回 400 expired拒绝被篡改的签名返回 400 tampering拒绝畸形格式非 base64/非 JSON 返回 400 invalid state format。5.2 OAuth 拒绝回调与重复/重连分支surfsense_backend/tests/integration/composio/test_oauth_callback.py覆盖两条关键分支test_callback_with_error_param_redirects_to_denied_page回调带erroraccess_denied时302 跳转到/dashboard/{workspace}/connectors/callback?errorcomposio_oauth_denied且不创建任何连接器test_second_oauth_for_same_toolkit_takes_reconnection_branch同一 toolkit 第二次 OAuth 不会新建连接器而是复用原有行、仅更新config[composio_connected_account_id]。5.3 文件夹列表、选中文件配置持久化与 auth-expired 分类surfsense_backend/tests/integration/composio/test_drive_folders_route.py覆盖了GET /api/v1/connectors/{id}/composio-drive/folders与PUT /api/v1/search-source-connectors/{id}两条路由根目录列表返回 canned 数据Projects文件夹、e2e-canary.txt等选中文件配置写库并回读一致当工具调用抛出 Token has been expired or revoked. (HTTP 401: invalid_grant) 时接口返回 400 且正文含 authentication、expired同时连接器config[auth_expired]被置为true。这种「Playwright 走主流程 pytest 打边界」的分层是文档要传达的核心工程思想。六、严格 FakeComposio SDK 的替身实现旅程测试之所以能不碰真实网络地跑通靠的是surfsense_backend/tests/e2e/fakes/composio_module.py这个严格替身strict fake。它的关键设计有四点6.1 sys.modules 劫持替换真实 SDKE2E 入口run_backend.py与run_celery.py会在任何生产代码import composio之前把 fake 模块注册进sys.modules[composio]见 tests/e2e/README.md。之后生产代码里所有from composio import Composio都会解析到本文件的Composio类。6.2 严格失败语义未知 API 立刻报错_StrictFakeMixin.__getattr__对任何未建模的属性访问抛出NotImplementedError并提示把该 surface 加到 fake 里。这意味着如果未来生产代码调用了 SDK 的新方法例如client.bulk_operations.runCI 会响亮地失败而不是静默穿透到真实 SDK。这是防止 fake 与真实 SDK 悄悄漂移的关键机制composio_module.py。6.3 场景分支一个 fake 演多种剧情fake 的行为由请求作用域的 ContextVar 驱动该值来自X-E2E-Scenario请求头composio_module.py。目前实现了happy默认、deniedOAuth 拒绝、auth_expired令牌过期三种场景connected_accounts.initiate在denied场景下直接构造带erroraccess_denied的 redirect URL模拟用户拒绝授权tools.execute在auth_expired场景下抛出与真实 SDK 一致的错误串Token has been expired or revoked. (HTTP 401: invalid_grant)供生产路由分类为认证失效。6.4 已建模的 Drive 工具面_Tools.execute目前为 Drive 实现了这些 slugcomposio_module.pyTool slug行为GOOGLEDRIVE_LIST_FILES解析 Drive 风格q查询folder_id in parents and trashed false、mimeType ! ...等从 fixture 按目录返回文件列表GOOGLEDRIVE_DOWNLOAD_FILE把 fixture 字节写入/tmp/surfsense-e2e-composio-downloads并返回本地路径模拟真实 SDK下载到本地文件再返回路径的行为GOOGLEDRIVE_GET_FILE_METADATA从所有目录 fixture 中按 id 查找元数据GOOGLEDRIVE_GET_CHANGES_START_PAGE_TOKEN/GOOGLEDRIVE_LIST_CHANGES返回固定的 startPageToken 与空变更列表GOOGLEDRIVE_GET_ABOUT返回 fake 邮箱用于连接器展示名称未知 slug 同样触发NotImplementedError——没有为某个 slug 建模的 handler 本身就是测试 bugcomposio_module.py。此外fake 还实现了_AuthConfigs.list()目前返回三个 toolkitgoogledrive、gmail、googlecalendar。七、passes不证明什么有意的边界文档对这套测试的盲区做了坦诚的声明这三件事刻意不在 Phase 1 的承诺范围内真实的 Composio.dev 集成——SDK 被严格 fake 替换测试从不发起真实网络请求真实的 LLM 摘要质量——聊天与摘要走FakeListChatModel见 tests/e2e/README.md 提到的fakes/llm.py不评估摘要语义好坏真实的嵌入语义——嵌入使用常数 0.1 向量fakes/embeddings.py不验证检索相关性。文档明确解释这些是有意为之Phase 1 的契约是用户可见的 Drive 旅程穿过连接器与索引的接缝至于真实 LLM 的冒烟测试Phase 2 可以在独立 workflow 与独立预算下以 opt-in 方式补充。这种先证明管线通、再证明质量好的节奏值得同类项目借鉴。八、如何新增第四个 Composio toolkit以 Slack 为例文档给出了扩展的完整操作手册共四步配合仓库现状可逐一对应1. 添加 fixture 数据在surfsense_backend/tests/e2e/fakes/fixtures/下新增toolkit_*.json。仓库里已有slack_messages.json等同类文件可参考若涉及二进制还需要在binary/下放置文件并在_file_binary_paths中登记。2. 扩展_Tools.execute()在 composio_module.py 的_Tools.execute中为新 toolkit 的 tool slug 增加处理分支例如SLACK_FETCH_CONVERSATIONS。务必遵守严格语义——未建模的 slug 必须抛NotImplementedError而不是静默放行。3. 将 toolkit 加入_AuthConfigs.list()在_AuthConfigs.list()的 items 列表中追加一条例如_AuthConfig(config_idauth-config-slack, toolkit_slugslack)当前已有 googledrive / gmail / googlecalendar 三条。4. 放置兄弟目录的 journey spec在surfsense_web/tests/connectors/composio/下新建toolkit/journey.spec.ts用一条 spec 匹配该 toolkit 的用户预期。仓库中calendar/、gmail/、drive/三个目录即为此模式。文档最后强调surfsense_web/tests/fixtures/connectors/composio-drive.fixture.ts是模板——复制后只需把toolkit_id从googledrive换成目标 toolkit如slackOAuth fixture、自动清理逻辑都可直接复用。九、本地运行与 hermetic 环境要亲自跑通这套旅程tests/e2e/README.md 给出了两条路径9.1 日常开发deps-only 快速流从仓库根目录启动 Postgres Redis其余 deps-only 服务如 Zero、pgAdmin 不需要docker compose -f docker/docker-compose.deps-only.yml up -d db redis然后在surfsense_backend/终端 A 启动后端uv sync uv run alembic upgrade head uv run python tests/e2e/run_backend.py终端 B 启动 Celery workeruv run python tests/e2e/run_celery.py注册 Playwright 用户curl -X POST http://localhost:8000/auth/register \ -H Content-Type: application/json \ -d {email:e2e-testsurfsense.net,password:E2eTestPassword123!}在surfsense_web/终端 C 运行 Playwrightpnpm test:e2e # dev server快速迭代 pnpm test:e2e:headed # 显示浏览器 pnpm test:e2e:ui # Playwright UI 模式 pnpm test:e2e:prod # build start与 CI 完全一致9.2 严格复现 CIhermetic 容器流docker/docker-compose.e2e.yml提供了与 CI 完全一致的环境internal: true的内部网络让 db/redis/celery_worker没有任何外网出口backend 单独挂一块ingress网桥让宿主机能访问:8000所有 API key 被替换为e2e-deny-real-call-sentinel哨兵值HTTPS_PROXY指向不可达端口作为纵深防御——即使有泄漏的真实 HTTP 调用也会立刻 Connection refused。用法docker compose -f docker/docker-compose.e2e.yml up -d --build --wait # ... 执行上面的 curl 注册与 pnpm test:e2e:prod ... docker compose -f docker/docker-compose.e2e.yml down -v --remove-orphansREADME 同时提醒该方式会构建约 9 GB 的surfsense-e2e-backend:local镜像日常开发优先使用 deps-only 流。另外tests/目录被.dockerignore排除、通过独立的tests-sourcebuild context 注入保证测试 fake 永远不会进入生产镜像见 docker-compose.e2e.yml。十、总结这条旅程测试的设计精髓回顾整个 Composio Drive E2E 体系可以提炼出三条可复用的工程原则用金丝雀令牌穿透整条管线不 mock 业务结果只 mock 外部依赖用哨兵字符串证明数据真的从文件变成了可检索的文档和可回显的聊天内容按成本分层主流程交给 Playwright真实 UI 交互边界与安全交给 pytestOAuth state、回调分支、错误分类各司其职fake 必须严格sys.modules劫持 未建模 surface 立刻失败让测试替身与真实 SDK 永不静默漂移同时用X-E2E-Scenario请求头让一个 fake 能演绎 happy / denied / auth_expired 多种剧情。如果你正在为 SurfSense 添加新的 Composio toolkit或者想为自己的项目搭建零外网依赖的云连接器 E2E 测试这条 journey strict fake canary 的组合套路就是最直接的参考实现。【免费下载链接】SurfSenseOpen-source NotebookLM alternative. Research the open web with live data(Reddit, YT, IG, TikTok, Indeed, Google Search, Maps etc) through one platform, API or MCP server. Join our Discord: https://discord.gg/ejRNvftDp9项目地址: https://gitcode.com/GitHub_Trending/su/SurfSense创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表