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

feat: add http api tests to nodejs sdk #1471

Merged
merged 5 commits into from
Nov 26, 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
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright [yyyy] [name of copyright owner]
Copyright 2024 Momento Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
5 changes: 3 additions & 2 deletions packages/client-sdk-nodejs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@
},
"scripts": {
"prebuild": "eslint . --ext .ts",
"test": "jest --testPathIgnorePatterns auth-client.test.ts --maxWorkers 1",
"test": "jest --testPathIgnorePatterns=\"auth-client\\.test\\.ts|http-apis\\.test\\.ts\" --maxWorkers 1",
anitarua marked this conversation as resolved.
Show resolved Hide resolved
"integration-test-auth": "jest auth/ --maxWorkers 1 -- useConsistentReads",
"integration-test-http": "jest http/ --maxWorkers 1 -- useConsistentReads",
"integration-test-cache": "jest cache/ --maxWorkers 1 -- useConsistentReads",
"integration-test-control-cache-topics": "npm run integration-test-cache && npm run integration-test-topics",
"integration-test-leaderboard": "jest leaderboard/ --maxWorkers 1 -- useConsistentReads",
Expand All @@ -26,7 +27,7 @@
"integration-test": "jest integration --maxWorkers 1",
"unit-test": "jest unit",
"build-deps": "cd ../core && npm run build && cd - && cd ../common-integration-tests && npm run build && cd -",
"build-and-run-tests": "npm run build-deps && jest --testPathIgnorePatterns auth-client.test.ts --maxWorkers 1",
"build-and-run-tests": "npm run build-deps && jest --testPathIgnorePatterns=\"auth-client\\.test\\.ts|http-apis\\.test\\.ts\" --maxWorkers 1",
"lint": "eslint . --ext .ts",
"format": "eslint . --ext .ts --fix",
"watch": "tsc -w",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ export function SetupIntegrationTest(): {
cacheClientWithBalancedReadConcern: CacheClient;
cacheClientWithConsistentReadConcern: CacheClient;
integrationTestCacheName: string;
credentialProvider: CredentialProvider;
} {
const cacheName = testCacheName();

Expand Down Expand Up @@ -238,6 +239,7 @@ export function SetupIntegrationTest(): {
cacheClientWithBalancedReadConcern: clientWithBalancedReadConcern,
cacheClientWithConsistentReadConcern: clientWithConsistentReadConcern,
integrationTestCacheName: cacheName,
credentialProvider: credsProvider(),
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {SetupIntegrationTest} from '../../integration-setup';
import {runHttpApiTest} from '@gomomento/common-integration-tests/dist/src/http/http-apis';

const {credentialProvider, integrationTestCacheName} = SetupIntegrationTest();

runHttpApiTest(credentialProvider, integrationTestCacheName);
5 changes: 3 additions & 2 deletions packages/client-sdk-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
},
"scripts": {
"prebuild": "eslint . --ext .ts",
"test": "jest --testPathIgnorePatterns auth-client.test.ts --maxWorkers 1",
"test": "jest --testPathIgnorePatterns=\"auth-client\\.test\\.ts|http-apis\\.test\\.ts\" --maxWorkers 1",
"unit-test": "jest unit",
"integration-test-auth": "jest --env=jsdom auth/ --maxWorkers 1 -- useConsistentReads",
"integration-test-http": "jest --env=jsdom http/ --maxWorkers 1 -- useConsistentReads",
"integration-test-cache": "jest --env=jsdom cache/ --maxWorkers 1 -- useConsistentReads",
"integration-test-control-cache-topics": "npm run integration-test-cache && npm run integration-test-topics",
"integration-test-leaderboard": "jest --env=jsdom leaderboard/ --maxWorkers 1 -- useConsistentReads",
Expand All @@ -30,7 +31,7 @@
"integration-test": "npm run integration-test-happy-dom && npm run integration-test-jsdom",
"integration-test-consistent-reads": "npm run integration-test-happy-dom-consistent-reads && npm run integration-test-jsdom-consistent-reads",
"build-deps": "cd ../core && npm run build && cd - && cd ../common-integration-tests && npm run build && cd -",
"build-and-run-tests": "npm run build-deps && jest --testPathIgnorePatterns auth-client.test.ts --maxWorkers 1",
"build-and-run-tests": "npm run build-deps && jest --testPathIgnorePatterns=\"auth-client\\.test\\.ts|http-apis\\.test\\.ts\" --maxWorkers 1",
"lint": "eslint . --ext .ts",
"format": "eslint . --ext .ts --fix",
"watch": "tsc -w",
Expand Down
2 changes: 2 additions & 0 deletions packages/client-sdk-web/test/integration/integration-setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export function SetupIntegrationTest(): {
cacheClientWithBalancedReadConcern: CacheClient;
cacheClientWithConsistentReadConcern: CacheClient;
integrationTestCacheName: string;
credentialProvider: CredentialProvider;
} {
const cacheName = testCacheName();

Expand Down Expand Up @@ -224,6 +225,7 @@ export function SetupIntegrationTest(): {
cacheClientWithBalancedReadConcern: clientWithBalancedReadConcern,
cacheClientWithConsistentReadConcern: clientWithConsistentReadConcern,
integrationTestCacheName: cacheName,
credentialProvider: credsProvider(),
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import {SetupIntegrationTest} from '../../integration-setup';
import {runHttpApiTest} from '@gomomento/common-integration-tests/dist/src/http/http-apis';

const {credentialProvider, integrationTestCacheName} = SetupIntegrationTest();

runHttpApiTest(credentialProvider, integrationTestCacheName);
206 changes: 206 additions & 0 deletions packages/common-integration-tests/src/http/http-apis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import {CredentialProvider} from '@gomomento/sdk-core';
import * as https from 'https';
import {v4} from 'uuid';

function makeRequest(
method: string,
hostname: string,
path: string,
headers: Record<string, string>,
body?: string
): Promise<{
statusCode: number;
statusMessage: string | undefined;
body: string;
}> {
return new Promise((resolve, reject) => {
const options = {
hostname,
path,
method,
headers,
};

const req = https.request(options, res => {
let data = '';

res.on('data', chunk => {
data += chunk;
});

res.on('end', () => {
resolve({
statusCode: res.statusCode || 0,
statusMessage: res.statusMessage,
body: data,
});
});
});

req.on('error', reject);

if (body) {
req.write(body);
}

req.end();
});
}

function putValue(
baseUrl: string,
apiKey: string,
cacheName: string,
key: string,
value: string,
ttl: number
): Promise<{statusCode: number; statusMessage: string | undefined}> {
const path = `/cache/${encodeValue(cacheName)}?key=${encodeValue(
key
)}&value=${encodeValue(value)}&ttl_seconds=${ttl}`;
const headers = {
Authorization: apiKey,
'Content-Type': 'application/json',
};

return makeRequest('PUT', baseUrl, path, headers, value);
}

function getValue(
baseUrl: string,
apiKey: string,
cacheName: string,
key: string
): Promise<{
statusCode: number;
statusMessage: string | undefined;
body: string;
}> {
const path = `/cache/${encodeValue(cacheName)}?key=${encodeValue(key)}`;
const headers = {
Authorization: apiKey,
};

return makeRequest('GET', baseUrl, path, headers);
}

function deleteValue(
baseUrl: string,
apiKey: string,
cacheName: string,
key: string
): Promise<{statusCode: number; statusMessage: string | undefined}> {
const path = `/cache/${encodeValue(cacheName)}?key=${encodeValue(key)}`;
const headers = {
Authorization: apiKey,
};

return makeRequest('DELETE', baseUrl, path, headers);
}

function encodeValue(value: string): string {
return encodeURIComponent(value);
}

export function runHttpApiTest(
credentialProvider: CredentialProvider,
cacheName: string
) {
describe('Momento HTTP API', () => {
let apiKey: string;
let baseUrl: string;
let cacheEndpoint: string;

beforeAll(() => {
cacheEndpoint = credentialProvider.getCacheEndpoint();
apiKey = credentialProvider.getAuthToken();
baseUrl = `api.${cacheEndpoint}`;
});

it('should return error on non-existing cache', async () => {
const key = v4();
const value = v4();
const ttl = 300;

const nonExistentCache = v4();

// PUT API
const putRes = await putValue(
baseUrl,
apiKey,
nonExistentCache,
key,
value,
ttl
);
expect(putRes.statusCode).toBe(404);
expect(putRes.statusMessage?.toLowerCase()).toBe('not found');

// GET API
const getRes = await getValue(baseUrl, apiKey, nonExistentCache, key);
expect(getRes.statusCode).toBe(404);
expect(getRes.statusMessage?.toLowerCase()).toBe('not found');

// DELETE API
const delRes = await deleteValue(baseUrl, apiKey, nonExistentCache, key);
expect(delRes.statusCode).toBe(404);
expect(delRes.statusMessage?.toLowerCase()).toBe('not found');
});

it('should successfully PUT and GET a value from a cache', async () => {
const key = v4();
const value = v4();
const ttl = 300;

// Use PUT API to set the string value
const putRes = await putValue(
baseUrl,
apiKey,
cacheName,
key,
value,
ttl
);
expect(putRes.statusCode).toBe(204);

// Use GET API to retrieve the value
const getRes = await getValue(baseUrl, apiKey, cacheName, key);
expect(getRes.statusCode).toBe(200);
expect(getRes.body).toBe(value);
});

it('should return error on GET on non-existing key', async () => {
// Use GET API to retrieve the value that was not set
const getRes2 = await getValue(baseUrl, apiKey, cacheName, v4());
expect(getRes2.statusCode).toBe(404);
expect(getRes2.statusMessage?.toLowerCase()).toBe('not found');
});

it('should successfully DELETE a value from a cache', async () => {
const key = v4();
const value = v4();
const ttl = 300;

// Use PUT API to set the value
const putRes = await putValue(
baseUrl,
apiKey,
cacheName,
key,
value,
ttl
);
expect(putRes.statusCode).toBe(204);

// Use DELETE API to delete the value that was set
const delRes = await deleteValue(baseUrl, apiKey, cacheName, key);
expect(delRes.statusCode).toBe(204);
});

it('should return success on DELETE on non-existing key', async () => {
// Use DELETE API to delete the value that was not set
const delRes2 = await deleteValue(baseUrl, apiKey, cacheName, v4());
expect(delRes2.statusCode).toBe(204);
});
});
}
Loading