Repository navigation
test(connections): add Connections integration tests [APPS-37829] - #761
anil-uipath wants to merge 1 commit into
Conversation
0307bd2 to
eb89a95
Compare
| expect(Array.isArray(result)).toBe(true); | ||
| // Assert the ordering the flag requests, not just that a response arrived — | ||
| // a shape-only assertion passes even when the flag never reaches the API. | ||
| const timestamps = result.map((connection) => Date.parse(connection.createTime)); | ||
| expect(timestamps.every((value) => Number.isFinite(value))).toBe(true); | ||
| const descending = [...timestamps].sort((a, b) => b - a); | ||
| expect(timestamps).toEqual(descending); |
There was a problem hiding this comment.
The ordering assertion passes vacuously when result has 0 or 1 items — a single-element array can never be out of order, and an empty array trivially satisfies toEqual. Per rules.md: "Always throw new Error() when test preconditions are not met." Add a minimum-count guard before the sort:
| expect(Array.isArray(result)).toBe(true); | |
| // Assert the ordering the flag requests, not just that a response arrived — | |
| // a shape-only assertion passes even when the flag never reaches the API. | |
| const timestamps = result.map((connection) => Date.parse(connection.createTime)); | |
| expect(timestamps.every((value) => Number.isFinite(value))).toBe(true); | |
| const descending = [...timestamps].sort((a, b) => b - a); | |
| expect(timestamps).toEqual(descending); | |
| expect(Array.isArray(result)).toBe(true); | |
| if (result.length < 2) { | |
| throw new Error( | |
| 'Need at least 2 connections to verify mostRecentFirst ordering — create more connections in the test tenant.', | |
| ); | |
| } | |
| // Assert the ordering the flag requests, not just that a response arrived — | |
| // a shape-only assertion passes even when the flag never reaches the API. | |
| const timestamps = result.map((connection) => Date.parse(connection.createTime)); | |
| expect(timestamps.every((value) => Number.isFinite(value))).toBe(true); | |
| const descending = [...timestamps].sort((a, b) => b - a); | |
| expect(timestamps).toEqual(descending); |
| }); | ||
|
|
||
| it('getObjectMetadata should return an object with name + fields', async () => { | ||
| const result = await elements.getObjectMetadata(elementKey, objectName); |
There was a problem hiding this comment.
Test description says "name + fields" but only result.name is asserted — result.fields is never checked. Per rules.md: "Test descriptions must match what's being tested." Either assert on result.fields (e.g. expect(Array.isArray(result.fields)).toBe(true)) or trim the description:
| const result = await elements.getObjectMetadata(elementKey, objectName); | |
| it('getObjectMetadata should return an object with name', async () => { |
|
|
||
| it('getInstanceObjectMetadata should return an object with name', async () => { | ||
| const result = await elements.getInstanceObjectMetadata(connectionId, elementKey, objectName); | ||
| expect(result).toBeDefined(); |
There was a problem hiding this comment.
objectName was derived from elements.getObjects(elementKey) (connector-level enumeration, line 41) but getInstanceObjectMetadata is a connection-scoped API. If the connection only exposes a subset of the connector's objects, this call can fail with a non-obvious error when objects[0].name happens not to be available under the specific connection.
A safer approach: resolve objectName for the instance tests from getInstanceObjects(connectionId, elementKey) instead, mirroring what the execute suite does (execution.integration.test.ts line 49):
const instanceObjects = await elements.getInstanceObjects(connectionId, elementKey);
if (instanceObjects.length === 0) {
throw new Error(`Connection ${connectionId} exposes no instance objects for ${elementKey}.`);
}
// Use connector-level objectName for static tests, connection-scoped for instance tests
const instanceObjectName = config.isObjectName ?? instanceObjects[0].name;Then use instanceObjectName in getInstanceObjectMetadata and getInstanceObjects tests.
|
Three findings from this run. New inline comments posted:
|
|
|
||
| it('getInstanceObjectMetadata should return an object with name', async () => { | ||
| const result = await elements.getInstanceObjectMetadata(connectionId, elementKey, objectName); | ||
| expect(result).toBeDefined(); |
There was a problem hiding this comment.
The four event tests each repeat the same if (!eventOperation) throw guard inline. Per agent_docs/rules.md:
"Consolidate service availability guards in
beforeAll", not inline in each test"
Since these guards all live inside describe('events'), a beforeAll at the top of that block would consolidate them into a single check and let each it body drop its guard entirely:
| expect(result).toBeDefined(); | |
| describe('events', () => { | |
| beforeAll(() => { | |
| if (!eventOperation) { | |
| throw new Error( | |
| 'INTEGRATION_SERVICE_TEST_EVENT_OPERATION must be set to exercise event endpoints', | |
| ); | |
| } | |
| }); | |
Each it body can then use eventOperation (and eventObjectName) without the repetitive guard.
| }); | ||
|
|
||
| it('should order results newest-first when mostRecentFirst is set', async () => { | ||
| const result = await connections.getAll({ pageSize: 3, mostRecentFirst: true }); |
There was a problem hiding this comment.
The ordering assertion is vacuously true when pageSize: 3 returns only one connection — a single-element array is always sorted. The mostRecentFirst flag would go completely unexercised in a tenant with a single connection.
Guard at the start of the test so the flag-vs-sort check is only reached when there are at least two data points:
| const result = await connections.getAll({ pageSize: 3, mostRecentFirst: true }); | |
| it('should order results newest-first when mostRecentFirst is set', async () => { | |
| const result = await connections.getAll({ pageSize: 3, mostRecentFirst: true }); | |
| expect(Array.isArray(result)).toBe(true); | |
| if (result.length < 2) { | |
| throw new Error( | |
| 'Test tenant has fewer than 2 connections — create at least two to validate mostRecentFirst ordering.', | |
| ); | |
| } |
Review summaryTwo new inline findings this run:
Everything else looks good: credential matrix, fixture wiring, CI pass-through, transform validation checks, and per-service |
eb89a95 to
99363d1
Compare
|
|
||
| const sample = result[0]; | ||
| expect(typeof sample.id).toBe('string'); | ||
| expect(typeof sample.name).toBe('string'); |
There was a problem hiding this comment.
The getById test validates transform completeness — camelCase fields present and PascalCase fields absent. The getAll test only validates camelCase presence. Per agent_docs/rules.md:
"
getAllandgetByIdneed separate transform completeness tests — each has its owntransformFnclosure, so agetByIdtest doesn't covergetAll."
Add the PascalCase absent assertions to the getAll shape check so both endpoints are independently verified:
| expect(typeof sample.name).toBe('string'); | |
| expect(Object.values(ConnectionState)).toContain(sample.state); | |
| // PascalCase API fields must be absent — validates the IS API returns camelCase verbatim | |
| const raw = sample as unknown as Record<string, unknown>; | |
| expect(raw.Id).toBeUndefined(); | |
| expect(raw.Name).toBeUndefined(); | |
| expect(raw.State).toBeUndefined(); |
| const sample = result[0]; | ||
| expect(typeof sample.id).toBe('number'); | ||
| expect(typeof sample.key).toBe('string'); | ||
| expect(typeof sample.name).toBe('string'); |
There was a problem hiding this comment.
The getById test validates camelCase fields present and PascalCase fields absent (with the comment "this validates the SDK returns the API shape verbatim"). The getAll test only checks field types. Per agent_docs/rules.md:
"
getAllandgetByIdneed separate transform completeness tests."
Add PascalCase absent assertions to make getAll a complete transform validation test, consistent with getById:
| expect(typeof sample.name).toBe('string'); | |
| expect(typeof sample.isPrivate).toBe('boolean'); | |
| // PascalCase keys must not be present — validates the IS API returns camelCase verbatim | |
| const raw = sample as unknown as Record<string, unknown>; | |
| expect(raw.Id).toBeUndefined(); | |
| expect(raw.Key).toBeUndefined(); | |
| expect(raw.Name).toBeUndefined(); | |
| expect(raw.IsPrivate).toBeUndefined(); |
|
Two new findings this run. New inline comments posted:
|
…[APPS-37829] Adds live-API suites for Connections, Connectors, Elements and Execute, and wires the fixtures they need through the integration harness and CI. Integration Service validates JWTs locally (`bearerFormat: JWT` in its own swagger), so an opaque `rt_` PAT fails at parse before any scope check and every call returns a bare 401 identical to an anonymous request. All four suites therefore declare `describeIntegration(..., 'user', ...)`: they run under UIPATH_USER_TOKEN and report as skipped on a PAT-only machine rather than failing. - register ConnectionsService / ConnectorsService / ElementsService in unified-setup, and add the four INTEGRATION_SERVICE_TEST_* fixtures to test-config - pass those fixtures through coverage.yml so the user leg of the credential matrix can run the suites - document the credential requirement and the fixtures in the integration README and .env.integration.example Refs APPS-37829 Co-Authored-By: Claude Opus 5 <[email protected]>
99363d1 to
9cd52b1
Compare
Summary
Adds live-API integration suites for the four Integration Service services already on
main(Connections #554, Connectors #555, Elements #556, Execute), and wires the fixtures they need through the integration harness and CI.No
src/changes — this is test-only. There are no new methods, endpoints, transforms or OAuth scopes, so the usual Method Added / Endpoint Called / API Response vs SDK Response sections don't apply; the equivalent information for a test PR is below.Suites Added
connections.integration.test.tsgetAll(),getById()connectors.integration.test.tsgetAll(),getById(),getConnections(),getDefaultConnection()elements.integration.test.tsgetObjects(),getActivities(),getObjectMetadata(),getEventObjects(),getEventObjectMetadata(),getInstanceObjects(),getInstanceObjectMetadata(),getInstanceEventObjects(),getInstanceEventObjectMetadata()execution.integration.test.tsexecute()Coverage is scoped to the API surface that actually exists on
main.Connections.ping()andConnections.reauthenticate()were both removed upstream (#698 and1b8f68ebe), so the suites exercisegetAll/getByIdonly — the entity has no bound methods to assert.Credential Requirement
All four suites are declared
describeIntegration(name, 'user', modes, body).Integration Service declares
bearerFormat: JWTin its own swagger and validates the JWT locally. The test tenant issues PATs in opaquert_form, so the token fails at parse before any scope check — a real PAT, a garbage string, and noAuthorizationheader at all return byte-identical401 {"code":"Unauthorized"}. A token that was being introspected but lacked a scope would return 403 /insufficient_scopeinstead.Consequence for the credential matrix in
coverage.yml:patconnections_at alluserUIPATH_USER_TOKEN(minted by Minter in CI)This follows the
'user'precedent already in the repo forinsightsrtm_(Agents, Agent Memory, Agent Traces, Governance) and the notification service, peragent_docs/rules.md: "When a service rejects PAT auth, run it under a user token — do notdescribe.skipit."How to Run
Without
UIPATH_USER_TOKENthe suites report as skipped rather than failing, which is what a PAT-only developer machine looks like by default.Fixtures
INTEGRATION_SERVICE_TEST_CONNECTOR_KEYuipath-salesforce-slackINTEGRATION_SERVICE_TEST_CONNECTION_IDINTEGRATION_SERVICE_TEST_EVENT_OPERATIONINTEGRATION_SERVICE_TEST_OBJECT_NAMEcoverage.ymlpasses all four through asUIPATH_INTEGRATION_SERVICE_TEST_*_DEV || UIPATH_INTEGRATION_SERVICE_TEST_*.Important
Three repository secrets must be created before this merges. Until they exist, the
userleg reachesbeforeAlland throwsINTEGRATION_SERVICE_TEST_… must be set:UIPATH_INTEGRATION_SERVICE_TEST_CONNECTOR_KEY_DEVUIPATH_INTEGRATION_SERVICE_TEST_CONNECTION_ID_DEVUIPATH_INTEGRATION_SERVICE_TEST_EVENT_OPERATION_DEVOBJECT_NAMEmay be left unset. The values point at resources in the alpha test tenant, so only the_DEVvariants are meaningful.Verification
npm run typechecknpm run lintnpm run test:unitnpm run build4 skipped (4)/23 of 23 tests skipped[v1][user]and reach the APItsconfig.jsonsets"exclude": [..., "tests", ...], sonpm run typecheckdoes not cover integration tests. These were additionally typechecked against a config that includestests/integration/**, which is how the API-surface mismatches above were found. That run is clean for these files; the 20 errors it reports elsewhere (Maestro suites,tests/utils/mocks/entities.ts,tests/utils/mocks/functions.ts,auth-errors) are pre-existing onmainand out of scope here — worth a follow-up, since nothing in CI currently sees them.A convention review pass against
agent_docs/was run over the diff and its findings folded into this commit: theis*-prefixed config fields were renamed to mirror their env vars 1:1 (integrationServiceTestConnectorKeyetc., sinceisConnectionIdreads as a boolean predicate), the Elements event-operation guard was consolidated intobeforeAll(it had been repeated in fouritblocks, andeventObjectName!carried a definite-assignment assertion the code did not honour), the unfiltered connector listing was hoisted intobeforeAll, and four assertions that could not fail were tightened —getDefaultConnectionnow assertsisDefault === truerather than its type, themostRecentFirsttest throws rather than passing vacuously on a single-element array,result.headersis checked for keys rather thantypeof === 'object'(which passes fornull), and two Elements test titles that promised unasserted fields were trimmed.A second pass verified each of those fixes and found more: the CI fixture block was still splitting the
IDENTITY_TEST_USER_ID/IDENTITY_MUTABLE_TEST_USER_IDpair in the.envheredoc (only the outputs block had been moved); neither Execute test could fail on a mis-built passthrough URL, since a 404 from a wrong endpoint constant satisfied both — the first now assertsstatus !== 404, which is the point of the suite;expect(Array.isArray(result))was tautological in five Elements tests because the service returnsresponse.data ?? [], so they now assert item shape; the Elements suite had no error-path coverage for any of its nine methods, sogetObjectMetadatanow has a reject test; two duplicate live lookups were hoisted intobeforeAll; and a comment documenting the object-name precedence had it backwards.Not yet run green against the live API — that needs a minted user token, which is only available in CI or from a local Minter run.
Files
tests/integration/shared/integration-service/connections.integration.test.ts(4 tests)tests/integration/shared/integration-service/connectors.integration.test.ts(7 tests)tests/integration/shared/integration-service/elements.integration.test.ts(10 tests)tests/integration/shared/integration-service/execution.integration.test.ts(2 tests)tests/integration/config/unified-setup.ts(registers the three services)tests/integration/config/test-config.ts(four fixtures).github/workflows/coverage.yml(fixture pass-through)tests/integration/README.md,tests/.env.integration.exampleRefs APPS-37829
🤖 Auto-generated using onboarding skills