Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: pass headers to request #130

Merged
merged 3 commits into from
Oct 31, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ The Getting Started section covers the following topics for technical interopera

**Note**: The preset configuration in the config file (`packages/vc-test-suite/config.ts`) has already been preconfigured to use the services set up with Docker Compose. If you're using the default Docker setup, you may not need to modify these settings.

**Note**: In the config file, you can specify headers to be included in the request for each test suite within the Tier 1 test suite (e.g. `RenderTemplate2024`, `Storage`, etc.). Each test case will inherit these headers from the respective config. This is particularly useful if you need to include an authorization token, such as `Bearer <token>`.

3. **Usage**: Learn how to run the various components of the technical interoperability test suite and interpret the results. This section will guide you through:
- Running the W3C V2 VCDM tests
- Executing UNTP extension tests
Expand All @@ -30,4 +32,4 @@ The Getting Started section covers the following topics for technical interopera

By the end of this section, you will have a solid foundation for installing, configuring, and using the UNTP Technical Interoperability Test Suite. You'll be able to validate your UNTP implementation's technical components against the required standards.

If you're using the default Docker setup, most of the configuration has been done for you. However, if you need to customise any settings or are using a different setup, the configuration section of each component will guide you through the necessary steps.
If you're using the default Docker setup, most of the configuration has been done for you. However, if you need to customise any settings or are using a different setup, the configuration section of each component will guide you through the necessary steps.
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,16 @@ To test your Storage implementation, follow these steps:
3. **View Test Results**:
- Navigate to `packages/vc-test-suite/reports/index.html`
- Open this file in a web browser
- Look for the "Storage Service" section to view your test results
- Look for the "Storage Service" section to view your test results

- `url`: This is the full URL for the unencrypted storage service endpoint of your storage service.

- `encryptionUrl`: This is the full URL for the encryption service endpoint of your storage service.

- `headers`: An object containing any additional HTTP headers required for the request to the storage service. You may need to add headers depending on your implementation.

- `method`: The HTTP method used to request the verifiable credential from the storage service. In this case, it's set to 'POST'.

- `additionalParams`: An object containing any additional parameters required for the request to the storage service.

- `additionalPayload`: An object containing any additional payload required for the request to the storage service.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ To test your Rendering implementation, follow these steps:
testSuites: {
RenderTemplate2024: {
url: 'http://localhost:3332/agent/renderCredential',
headers: {},
headers: { Authorization: 'Bearer test123' },
method: 'POST',
},
},
Expand All @@ -105,3 +105,9 @@ To test your Rendering implementation, follow these steps:
- Navigate to `packages/vc-test-suite/reports/index.html`
- Open this file in a web browser
- Look for the "RenderTemplate2024" section to view your test results

- `url`: This is the full URL for the credential renderer endpoint of your verifiable credential service.

- `headers`: An object containing any additional HTTP headers required for the request to the verifiable credential service. You may need to add headers depending on your implementation.

- `method`: The HTTP method used to request the rendered credential from the verifiable credential service. In this case, it's set to 'POST'.
4 changes: 2 additions & 2 deletions packages/services/src/vckit.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '@vckit/core-types';
import { decodeJwt } from 'jose';
import { privateAPI } from './utils/httpService.js';
import { isPlainObject, every, isString } from 'lodash';
import _ from 'lodash';

export const contextDefault = [
'https://www.w3.org/ns/credentials/v2',
Expand Down Expand Up @@ -208,7 +208,7 @@ export const decodeEnvelopedVC = (vc: VerifiableCredential): UnsignedCredential
* @param headers
*/
const _validateVckitHeaders = (headers: Record<string, string>) => {
if (!isPlainObject(headers) || !every(headers, isString)) {
if (!_.isPlainObject(headers) || !_.every(headers, (value) => _.isString(value))) {
throw new Error('VcKit headers defined in app config must be a plain object with string values');
}
};
52 changes: 52 additions & 0 deletions packages/vc-test-suite/__tests__/httpService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { request } from '../httpService';
import axios from 'axios';

jest.mock('axios');

const mockedAxios = axios as jest.Mocked<typeof axios>;

describe('httpService', () => {
ashleythedeveloper marked this conversation as resolved.
Show resolved Hide resolved
beforeEach(() => {
jest.clearAllMocks();

const mockRequest = jest.fn().mockResolvedValue({ data: 'mocked response' });

mockedAxios.create.mockReturnValue({
request: mockRequest,
} as any);
});

it('should accept a plain object for headers', async () => {
const params = { headers: { test: 'test' } };
const result = await request(params);

expect(result).toEqual({ data: 'mocked response' });

const mockAxiosInstance = mockedAxios.create.mock.results[0].value;
expect(mockAxiosInstance.request).toHaveBeenCalledTimes(1);
expect(mockAxiosInstance.request).toHaveBeenCalledWith(params);
});

it('should throw an error if headers is not a plain object', async () => {
await expect(request({ headers: [] as any })).rejects.toThrow(
'Headers specified in the config must be a plain object with string values.',
);
});

it('should throw an error if headers contain non-string values', async () => {
await expect(request({ headers: { test: 123 } as any })).rejects.toThrow(
'Headers specified in the config must be a plain object with string values.',
);
});

it('should work when no headers are provided in params', async () => {
const params = { url: 'test-url' };
const result = await request(params);

expect(result).toEqual({ data: 'mocked response' });

const mockAxiosInstance = mockedAxios.create.mock.results[0].value;
expect(mockAxiosInstance.request).toHaveBeenCalledTimes(1);
expect(mockAxiosInstance.request).toHaveBeenCalledWith(params);
});
});
2 changes: 1 addition & 1 deletion packages/vc-test-suite/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default {
},
RenderTemplate2024: {
url: 'http://localhost:3332/agent/renderCredential',
headers: {},
headers: { Authorization: 'Bearer test123' },
method: 'POST',
},
Storage: {
Expand Down
12 changes: 11 additions & 1 deletion packages/vc-test-suite/httpService.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import axios, { AxiosRequestConfig } from 'axios';
import _ from 'lodash';
namhoang1604 marked this conversation as resolved.
Show resolved Hide resolved

const defaultHeaders = {
'Content-Type': 'application/json',
};

export const request = async (params: AxiosRequestConfig) => {
if (params.headers) {
ashleythedeveloper marked this conversation as resolved.
Show resolved Hide resolved
if (!_.isPlainObject(params.headers) || !_.every(params.headers, (value) => _.isString(value))) {
throw new Error('Headers specified in the config must be a plain object with string values.');
}
}

const instance = axios.create({
headers: defaultHeaders,
headers: {
...defaultHeaders,
...params.headers,
},
});

const response = await instance.request(params);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,54 +1,55 @@
import { decryptCredential } from '@mock-app/services';
import chai from 'chai';
import * as config from '../../config';
import config from '../../config';
import { reportRow, setupMatrix } from '../../helpers';
import { request } from '../../httpService';
import { isURLEncoded, parseQRLink } from './helper';

const expect = chai.expect;

describe('QR Link Verification', function () {
const qrLink = config.default.testSuites.QrLinkEncrypted.url;
const method = config.default.testSuites.QrLinkEncrypted.method;
const { url: qrLink, method, headers } = config.testSuites.QrLinkEncrypted;
const parsedLink = parseQRLink(qrLink);

setupMatrix.call(this, [config.default.implementationName], 'Implementer');
setupMatrix.call(this, [config.implementationName], 'Implementer');

reportRow('QR link MUST be URL encoded', config.default.implementationName, function () {
reportRow('QR link MUST be URL encoded', config.implementationName, function () {
const data = isURLEncoded(qrLink);
data.should.be.true;
});

reportRow('Verification page link MUST exist and be a string', config.default.implementationName, function () {
reportRow('Verification page link MUST exist and be a string', config.implementationName, function () {
expect(parsedLink.verify_app_address).to.be.a('string');
});

reportRow('Payload MUST exist and be an object', config.default.implementationName, function () {
reportRow('Payload MUST exist and be an object', config.implementationName, function () {
expect(parsedLink.q.payload).to.be.an('object');
});

reportRow('URI MUST exist and be a string', config.default.implementationName, function () {
reportRow('URI MUST exist and be a string', config.implementationName, function () {
expect(parsedLink.q.payload.uri).to.be.a('string');
});

reportRow('URI MUST be resolvable', config.default.implementationName, async function () {
reportRow('URI MUST be resolvable', config.implementationName, async function () {
const { data } = await request({
url: parsedLink.q.payload.uri,
method,
headers,
});

data.should.be.an('object');
});

reportRow('Hash MUST exist and be a string', config.default.implementationName, function () {
reportRow('Hash MUST exist and be a string', config.implementationName, function () {
expect(parsedLink.q.payload.hash).to.be.a('string');
});

reportRow('Hash MUST match the credential hash', config.default.implementationName, async function () {
reportRow('Hash MUST match the credential hash', config.implementationName, async function () {
// TODO: Implement this test case with hash comparison
// const res = await request({
// url: parsedLink.q.payload.uri,
// method: 'GET',
// headers,
// });
// const stringifyVC = decryptCredential({
// ...res.document,
Expand All @@ -59,14 +60,15 @@ describe('QR Link Verification', function () {
// expect(parsedLink.q.payload.hash).to.equal(credentialHash);
});

reportRow('Key exist and be a string', config.default.implementationName, function () {
reportRow('Key exist and be a string', config.implementationName, function () {
expect(parsedLink.q.payload.key).to.be.a('string');
});

reportRow('Key MUST decrypt the encrypted credential', config.default.implementationName, async function () {
reportRow('Key MUST decrypt the encrypted credential', config.implementationName, async function () {
const { data } = await request({
url: parsedLink.q.payload.uri,
method,
headers,
});

const stringifyVC = decryptCredential({
Expand Down
Loading
Loading