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
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@ jobs:
- name: Run integration tests
run: pnpm test # Make sure this command runs your integration tests
env:
AUTHORIZER_IMAGE: quay.io/authorizer/authorizer:2.4.0-rc.9
AUTHORIZER_IMAGE: quay.io/authorizer/authorizer:2.4.0-rc.13
22 changes: 13 additions & 9 deletions __test__/admin.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('Integration Tests - AuthorizerAdmin (graphql + rest)', () => {
const { args } = buildAuthorizerCliArgs();

container = await new GenericContainer(
process.env.AUTHORIZER_IMAGE || 'quay.io/authorizer/authorizer:2.4.0-rc.1',
process.env.AUTHORIZER_IMAGE || 'quay.io/authorizer/authorizer:2.4.0-rc.13',
)
.withCommand(args)
.withExposedPorts(8080)
Expand Down Expand Up @@ -112,14 +112,18 @@ describe('Integration Tests - AuthorizerAdmin (graphql + rest)', () => {
).toThrow(/grpc/);
});

it('rejects rest-only methods over graphql with a clear error', async () => {
const admin = adminFor('graphql');
const res = await admin.adminMeta();
expect(res.data).toBeUndefined();
expect(res.errors.length).toBeGreaterThan(0);
expect(res.errors[0].message).toMatch(
/AdminMeta is not available over graphql/,
);
// adminMeta used to be rest-only in the SDK even though `_admin_meta` exists
// on the server; it now works over both, returning the same payload either
// way. REST nests it under admin_meta while GraphQL returns it directly, so
// a wrong unwrap shows up here as a missing roles array.
it('adminMeta agrees over graphql and rest', async () => {
const viaGql = await adminFor('graphql').adminMeta();
expect(viaGql.errors).toHaveLength(0);
expect(viaGql.data?.roles?.length).toBeGreaterThan(0);

const viaRest = await adminFor('rest').adminMeta();
expect(viaRest.errors).toHaveLength(0);
expect(viaRest.data).toEqual(viaGql.data);
});

it('rejects graphql-only methods over rest with a clear error', async () => {
Expand Down
2 changes: 1 addition & 1 deletion __test__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ describe('Integration Tests - authorizer-js', () => {
// Override with AUTHORIZER_IMAGE to test against a different server build
// (e.g. a locally built image with newer GraphQL surface).
container = await new GenericContainer(
process.env.AUTHORIZER_IMAGE || 'quay.io/authorizer/authorizer:2.4.0-rc.1',
process.env.AUTHORIZER_IMAGE || 'quay.io/authorizer/authorizer:2.4.0-rc.13',
)
.withCommand(args)
.withExposedPorts(8080)
Expand Down
47 changes: 43 additions & 4 deletions __test__/tokenGrants.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,13 +230,52 @@ describe('AuthorizerAdmin machine-agent-identity methods', () => {
expect(lastRequest().url).toBe('http://localhost:8080/v1/admin/client');
});

it('graphql-only org methods refuse the rest protocol with a clear error', async () => {
// Organizations / org SSO / SCIM / org domains gained REST routes in server
// 2.4.0; they used to refuse the rest protocol outright. The wrapper the
// gateway puts around the payload differs per endpoint, so both shapes are
// pinned here: a single nested object is unwrapped, a paginated list is not.
it('createOrganization unwraps the proto-gateway wrapper over rest', async () => {
const admin = new AuthorizerAdmin({ ...adminConfig, protocol: 'rest' });
mockJsonResponse({ organization: { id: 'o1', name: 'acme' } });
const res = await admin.createOrganization({ name: 'acme' });
expect(res.errors[0].message).toBe(
'CreateOrganization is not available over rest; supported: graphql',
expect(res.errors).toHaveLength(0);
expect(res.data).toEqual({ id: 'o1', name: 'acme' });

const { url, body } = lastRequest();
expect(url).toBe('http://localhost:8080/v1/admin/create_organization');
expect(body).toEqual({ name: 'acme' });
});

it('organizations reads the paginated list whole over rest', async () => {
const admin = new AuthorizerAdmin({ ...adminConfig, protocol: 'rest' });
mockJsonResponse({
organizations: [{ id: 'o1', name: 'acme' }],
pagination: { limit: '10', page: '1', offset: '0', total: '1' },
});
const res = await admin.organizations();
expect(res.errors).toHaveLength(0);
expect(res.data?.organizations?.[0].id).toBe('o1');
expect(res.data?.pagination.total).toBe(1);
expect(lastRequest().url).toBe(
'http://localhost:8080/v1/admin/organizations',
);
expect(fetchMock).not.toHaveBeenCalled();
});

it('scimEndpoint unwraps while createScimEndpoint keeps the one-time token', async () => {
const admin = new AuthorizerAdmin({ ...adminConfig, protocol: 'rest' });
mockJsonResponse({ scim_endpoint: { id: 's1', org_id: 'o1' } });
const got = await admin.scimEndpoint({ org_id: 'o1' });
expect(got.data).toEqual({ id: 's1', org_id: 'o1' });

// create carries endpoint AND token side by side, so it is read whole -
// unwrapping either field would silently drop the other.
mockJsonResponse({
scim_endpoint: { id: 's1', org_id: 'o1' },
token: 'bearer-once',
});
const created = await admin.createScimEndpoint({ org_id: 'o1' });
expect(created.data?.token).toBe('bearer-once');
expect(created.data?.scim_endpoint?.id).toBe('s1');
});

it('createScimEndpoint posts the _create_scim_endpoint mutation', async () => {
Expand Down
Loading
Loading