Skip to content

Commit

Permalink
feat(platforms,database-client): fix tests for google platform & data…
Browse files Browse the repository at this point in the history
…base client
  • Loading branch information
nutrina committed Nov 17, 2022
1 parent 7305f3f commit 4154640
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 204 deletions.
39 changes: 29 additions & 10 deletions database-client/__tests__/CeramicDatabase.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import testnetAliases from "./integration-test-model-aliases.json";
import { TileDocument } from "@ceramicnetwork/stream-tile";
import { TileDoc } from "@glazed/did-datastore/dist/proxy";

import axios from "axios";

let testDID: DID;
let ceramicDatabase: CeramicDatabase;

Expand Down Expand Up @@ -87,12 +89,21 @@ describe("Verify Ceramic Database", () => {
id: "passport-id",
} as unknown as TileDoc;
});
let spyLoaderLoad = jest.spyOn(ceramicDatabase.loader, "load").mockImplementation(async (streamId) => {
// return new TileDocument(new SnapshotState(), );
return {
content: "Stamp Content for " + streamId,
} as any as TileDocument;
const spyLoadStreamReq = jest.spyOn(axios, "get").mockImplementation((url: string): Promise<{}> => {
return new Promise((resolve) => {
const urlSegments = url.split("/");
const streamId = urlSegments[urlSegments.length - 1];
resolve({
data: {
state: {
content: "Stamp Content for ceramic://" + streamId,
},
},
status: 200,
});
});
});

let spyPinAdd = jest.spyOn(ceramicDatabase.ceramicClient.pin, "add").mockImplementation(async (streamId) => {
// Nothing to do here
return;
Expand All @@ -103,7 +114,7 @@ describe("Verify Ceramic Database", () => {
// We do not expect to have any passport, hence `false` should be returned
expect(spyStoreGet).toBeCalledTimes(1);
expect(spyStoreGet).toBeCalledWith("Passport");
expect(spyLoaderLoad).toBeCalledTimes(3);
expect(spyLoadStreamReq).toBeCalledTimes(3);
expect(spyStoreGetRecordDocument).toBeCalledTimes(1);

// Ensure the document is pinned
Expand Down Expand Up @@ -164,17 +175,25 @@ describe("Verify Ceramic Database", () => {
id: "passport-id",
} as unknown as TileDoc;
});
let spyLoaderLoad = jest.spyOn(ceramicDatabase.loader, "load").mockImplementation(async (streamId) => {
const spyLoadStreamReq = jest.spyOn(axios, "get").mockImplementation((url: string): Promise<{}> => {
return new Promise((resolve, reject) => {
const urlSegments = url.split("/");
const streamId = urlSegments[urlSegments.length - 1];
if (numGoodStamps < maxGoodStamps) {
numGoodStamps += 1;
resolve({
content: "Stamp Content for " + streamId,
} as any as TileDocument);
data: {
state: {
content: "Stamp Content for ceramic://" + streamId,
},
},
status: 200,
});
}
reject("Error loading stamp!");
});
});

let spyPinAdd = jest.spyOn(ceramicDatabase.ceramicClient.pin, "add").mockImplementation(async (streamId) => {
// Nothing to do here
return;
Expand All @@ -185,7 +204,7 @@ describe("Verify Ceramic Database", () => {
// We do not expect to have any passport, hence `false` should be returned
expect(spyStoreGet).toBeCalledTimes(1);
expect(spyStoreGet).toBeCalledWith("Passport");
expect(spyLoaderLoad).toBeCalledTimes(3);
expect(spyLoadStreamReq).toBeCalledTimes(3);
expect(spyStoreGetRecordDocument).toBeCalledTimes(1);

// Ensure the document is pinned
Expand Down
3 changes: 1 addition & 2 deletions database-client/src/ceramicClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,7 @@ export class CeramicDatabase implements DataStorageBase {
this.logger.error(
`Error when loading stamp with streamId ${streamIDs[idx]} for did ${this.did}:` + e.toString()
);
console.log(e);
return null;
throw e;
}
});

Expand Down
84 changes: 0 additions & 84 deletions iam/__tests__/google.test.ts

This file was deleted.

1 change: 0 additions & 1 deletion iam/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import { Providers } from "./utils/providers";

// ---- Identity Providers
import { SimpleProvider } from "./providers/simple";
import { GoogleProvider } from "./providers/google";

import {
Twitter,
Expand Down
170 changes: 170 additions & 0 deletions platforms/src/Google/Providers/__tests__/google.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// ---- Test subject
import { RequestPayload } from "@gitcoin/passport-types";
import * as google from "../google";

const MOCK_EMAIL = "testEmail";
const MOCK_EMAIL_VERIFIED = true;
const MOCK_TOKEN_ID = "testToken";
const MOCK_ACCESS_TOKEN = "secret access token";

import axios from "axios";

describe("Attempt verification", function () {
beforeEach(() => {
jest.restoreAllMocks();
});

it("handles valid verification attempt", async () => {
const googleProvider = new google.GoogleProvider();

const verifyGoogleMock = jest
.spyOn(google, "verifyGoogle")
.mockImplementation((code: string): Promise<google.GoogleResponse> => {
return new Promise<google.GoogleResponse>((resolve) => {
resolve({
email: MOCK_EMAIL,
emailVerified: MOCK_EMAIL_VERIFIED,
});
});
});

const verifiedPayload = await googleProvider.verify({
proofs: {
code: MOCK_TOKEN_ID,
},
} as unknown as RequestPayload);

expect(verifyGoogleMock).toBeCalledWith(MOCK_TOKEN_ID);
expect(verifiedPayload).toEqual({
valid: true,
record: {
email: MOCK_EMAIL,
},
});
});

it("should return invalid payload when email is not verified", async () => {
const googleProvider = new google.GoogleProvider();
const verifyGoogleMock = jest
.spyOn(google, "verifyGoogle")
.mockImplementation((code: string): Promise<google.GoogleResponse> => {
return new Promise<google.GoogleResponse>((resolve) => {
resolve({
email: MOCK_EMAIL,
emailVerified: false,
});
});
});

const verifiedPayload = await googleProvider.verify({
proofs: {
code: MOCK_TOKEN_ID,
},
} as unknown as RequestPayload);

expect(verifyGoogleMock).toBeCalledWith(MOCK_TOKEN_ID);
expect(verifiedPayload).toEqual({
valid: false,
record: {
email: MOCK_EMAIL,
},
});
});

it("should return invalid payload when verifyGoogle throws exception", async () => {
const googleProvider = new google.GoogleProvider();
const verifyGoogleMock = jest
.spyOn(google, "verifyGoogle")
.mockImplementation((code: string): Promise<google.GoogleResponse> => {
throw Error("ERROR!!!");
});

const verifiedPayload = await googleProvider.verify({
proofs: {
code: MOCK_TOKEN_ID,
},
} as unknown as RequestPayload);

expect(verifyGoogleMock).toBeCalledWith(MOCK_TOKEN_ID);
expect(verifiedPayload).toEqual({
valid: false,
});
});
});

describe("verifyGoogle", function () {
beforeEach(() => {
jest.restoreAllMocks();
});

it("should suceed when a access token and user info are obtained", async () => {
const requestAccessTokenMock = jest
.spyOn(google, "requestAccessToken")
.mockImplementation((code: string): Promise<string> => {
return new Promise((resolve) => {
resolve(MOCK_ACCESS_TOKEN);
});
});

const userInfoMock = jest.spyOn(axios, "get").mockImplementation((code: string): Promise<{}> => {
return new Promise((resolve) => {
resolve({
data: {
email: MOCK_EMAIL,
verified_email: MOCK_EMAIL_VERIFIED,
},
status: 200,
});
});
});

const verifiedGoogleResponse = await google.verifyGoogle(MOCK_TOKEN_ID);
expect(requestAccessTokenMock).toBeCalledWith(MOCK_TOKEN_ID);
expect(userInfoMock).toBeCalledWith("https://www.googleapis.com/oauth2/v2/userinfo", {
headers: { Authorization: `Bearer ${MOCK_ACCESS_TOKEN}` },
});
expect(verifiedGoogleResponse).toEqual({
email: MOCK_EMAIL,
emailVerified: MOCK_EMAIL_VERIFIED,
});
});

it("should throw if getting user info throws", async () => {
const requestAccessTokenMock = jest
.spyOn(google, "requestAccessToken")
.mockImplementation((code: string): Promise<string> => {
return new Promise((resolve) => {
resolve(MOCK_ACCESS_TOKEN);
});
});

const userInfoMock = jest.spyOn(axios, "get").mockImplementation((code: string): Promise<{}> => {
throw Error("USER INFO ERROR");
});

try {
const verifiedGoogleResponse = await google.verifyGoogle(MOCK_TOKEN_ID);
} catch (e) {
expect(e).toEqual(Error("USER INFO ERROR"));
}
expect(requestAccessTokenMock).toBeCalledWith(MOCK_TOKEN_ID);
expect(userInfoMock).toBeCalledWith("https://www.googleapis.com/oauth2/v2/userinfo", {
headers: { Authorization: `Bearer ${MOCK_ACCESS_TOKEN}` },
});
});

it("should throw when requestAccessToken throws", async () => {
const requestAccessTokenMock = jest
.spyOn(google, "requestAccessToken")
.mockImplementation((code: string): Promise<string> => {
throw Error("ERROR");
});

try {
const verifiedGoogleResponse = await google.verifyGoogle(MOCK_TOKEN_ID);
fail("Should have thrown ...");
} catch (e) {
expect(e).toEqual(Error("ERROR"));
}
});
});
15 changes: 3 additions & 12 deletions platforms/src/Google/Providers/google.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import type { Provider, ProviderOptions } from "../../types";
import axios from "axios";

// Checking a valid tokenId for a result from Google will result in the following type
type GoogleResponse = {
export type GoogleResponse = {
email?: string;
emailVerified?: boolean;
};
Expand Down Expand Up @@ -76,26 +76,17 @@ export const requestAccessToken = async (code: string): Promise<string> => {
};

// Perform verification on shared google access token
async function verifyGoogle(code: string): Promise<GoogleResponse> {
export const verifyGoogle = async (code: string): Promise<GoogleResponse> => {
// retrieve user's auth bearer token to authenticate client
console.log("geri get token ...");
const accessToken = await requestAccessToken(code);

console.log("geri accessToken", accessToken);

// Now that we have an access token fetch the user details
const userRequest = await axios.get("https://www.googleapis.com/oauth2/v2/userinfo", {
headers: { Authorization: `Bearer ${accessToken}` },
});

if (userRequest.status != 200) {
throw `Get user request returned status code ${userRequest.status} instead of the expected 200`;
}

console.log("userRequest.data", userRequest.data);

return {
email: userRequest.data.email,
emailVerified: userRequest.data.verified_email,
};
}
};
Loading

0 comments on commit 4154640

Please sign in to comment.