test(table): pin the executor auth pairing on the table read route (#6603)

* test(table): pin the executor auth pairing on the table read route

fetchTableSchema reaches GET /api/table/[tableId] with a legacy type:'internal'
token, which only works while that route authenticates through
checkSessionOrInternalAuth. Its sibling table routes already moved to the
delegation policy, which rejects that token outright, so migrating this one
without moving the caller in the same change would break every table tool on an
Agent block.

Assert the route still authenticates through the legacy path so that migration
fails here first, and record on the caller why it is deliberately not on
buildExecutorDelegationHeaders yet.

* test(table): assert the Bearer header reaches the legacy verifier

Address review: the pairing test sent no Authorization header and its name
claimed to verify token acceptance, which is pinned separately in
lib/auth/internal.test.ts. Send a representative header, assert it reaches
checkSessionOrInternalAuth unmodified, and scope the name and docs to what this
guard actually covers — the route's choice of verifier.
This commit is contained in:
Waleed
2026-08-11 22:09:15 -07:00
committed by GitHub
parent 0d640aabbc
commit e65345b63d
2 changed files with 58 additions and 1 deletions
+45 -1
View File
@@ -55,7 +55,7 @@ vi.mock('@/app/api/table/utils', () => ({
tableLockErrorResponse: () => null,
}))
import { PATCH } from '@/app/api/table/[tableId]/route'
import { GET, PATCH } from '@/app/api/table/[tableId]/route'
const TABLE = {
id: 'tbl_1',
@@ -162,3 +162,47 @@ describe('PATCH /api/table/[tableId] folder moves', () => {
expect(mockRenameTable).not.toHaveBeenCalled()
})
})
/**
* Pins which auth path this route hands a Bearer token to.
*
* `fetchTableSchema` in `@/tools/schema-enrichers` reaches this route with a legacy
* `type: 'internal'` token from the deprecated `buildAuthHeaders`. Only
* `checkSessionOrInternalAuth` accepts that token; the delegation policy the sibling
* table routes use rejects it outright. Migrating this route without moving that caller
* to `buildExecutorDelegationHeaders` in the same change breaks every table tool on an
* Agent block, so this fails first and names the caller.
*
* Scope: this pins the *route's* choice of verifier. That the legacy token is actually
* valid for that verifier — and rejected by the delegation one — is pinned separately in
* `@/lib/auth/internal.test.ts`. Both halves are needed; neither implies the other.
*/
describe('GET /api/table/[tableId] executor auth pairing', () => {
beforeEach(() => {
vi.clearAllMocks()
hybridAuthMockFns.mockCheckSessionOrInternalAuth.mockResolvedValue({
success: true,
userId: 'user-1',
authType: 'internal_jwt',
})
mockCheckAccess.mockResolvedValue({ ok: true, table: TABLE })
mockGetLimits.mockResolvedValue({ maxRowsPerTable: 1000 })
})
it('routes a Bearer token to the legacy verifier fetchTableSchema mints for', async () => {
const request = new NextRequest('http://localhost:3000/api/table/tbl_1?workspaceId=workspace-1')
request.headers.set('authorization', 'Bearer legacy-internal-token')
const response = await GET(request, routeContext)
expect(response.status).toBe(200)
expect(hybridAuthMockFns.mockCheckSessionOrInternalAuth).toHaveBeenCalledWith(
expect.objectContaining({
headers: expect.objectContaining({ get: expect.any(Function) }),
}),
expect.anything()
)
const [forwarded] = hybridAuthMockFns.mockCheckSessionOrInternalAuth.mock.calls[0]
expect(forwarded.headers.get('authorization')).toBe('Bearer legacy-internal-token')
})
})
+13
View File
@@ -7,6 +7,19 @@ import type { WorkflowToolExecutionContext } from '@/tools/types'
const logger = createLogger('SchemaEnrichers')
/**
* Reads a table's schema as the acting user.
*
* Deliberately still on the deprecated `buildAuthHeaders`, unlike its siblings in this
* file: `GET /api/table/[tableId]` authenticates through `checkSessionOrInternalAuth`,
* which accepts a legacy `type: 'internal'` token and rejects an executor delegation.
* Swapping this to `buildExecutorDelegationHeaders` before that route migrates would
* break every table tool on an Agent block. The route's own test pins the pairing.
*
* Unlike the workflow and knowledge enrichers, a failure here is loud — this runs as a
* tool-level `toolEnrichment`, so `createLLMToolSchema` surfaces it as a
* `ToolSchemaEnrichmentError` naming the tool rather than degrading the schema silently.
*/
async function fetchTableSchema(
tableId: string,
context: WorkflowToolExecutionContext