Skip to content

Commit

Permalink
split tests up into separate files
Browse files Browse the repository at this point in the history
  • Loading branch information
akdombrowski committed Nov 11, 2023
1 parent f84bef9 commit b7e8c31
Show file tree
Hide file tree
Showing 7 changed files with 784 additions and 765 deletions.
2 changes: 1 addition & 1 deletion cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export const cli = async (clipboard, argv) => {
} catch (e) {
console.error("I found an error :(");
console.error(
"Couldn't decode what was in your clipboard. Try copying the JWT again."
"Couldn't decode what was in your clipboard. You might try copying the JWT again."
);
console.error("What's the problem? ");
console.error(" ", e.message);
Expand Down
55 changes: 55 additions & 0 deletions test/base64URLEncode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { base64URLEncode } from "../src/index.js";
import crypto from "crypto";
import { expect } from "./index.js";

describe("#base64URLEncode()", function () {
context("when string literal is used", function () {
it("correctly base64url encodes the input", function () {
const rndBytes = crypto.randomBytes(128);
const rndBytesHexString = rndBytes.toString("hex");
const base64URLEncoded = base64URLEncode(rndBytesHexString);
const base64URLEncodedFromBuffer = base64URLEncode(rndBytes);
const bufferTransformed = Buffer.from(
rndBytesHexString,
"ascii"
).toString("base64url");
const bufferTransformedFromBuffer = Buffer.from(
rndBytes,
"ascii"
).toString("base64url");
expect(
base64URLEncoded,
"base64URLEncode returns the same thing as transforming with Buffer"
).to.be.equal(bufferTransformed);
expect(
base64URLEncodedFromBuffer,
"encodes from Buffer object"
).to.be.equal(bufferTransformedFromBuffer);
});
});

context("when Buffer is used", function () {
it("correctly base64url encodes the input", function () {
const rndBytes = crypto.randomBytes(128);
const rndBytesHexString = rndBytes.toString("hex");
const base64URLEncoded = base64URLEncode(rndBytesHexString);
const base64URLEncodedFromBuffer = base64URLEncode(rndBytes);
const bufferTransformed = Buffer.from(
rndBytesHexString,
"ascii"
).toString("base64url");
const bufferTransformedFromBuffer = Buffer.from(
rndBytes,
"ascii"
).toString("base64url");
expect(
base64URLEncoded,
"base64URLEncode returns the same thing as transforming with Buffer"
).to.be.equal(bufferTransformed);
expect(
base64URLEncodedFromBuffer,
"encodes from Buffer object"
).to.be.equal(bufferTransformedFromBuffer);
});
});
});
86 changes: 86 additions & 0 deletions test/bothJWTEncodeAndDecode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
jwtDecode,
jwtEncode,
hs256Sign,
createHeaderPayload,
} from "../src/index.js";
import { expect } from "./index.js";

describe("#jwtEncode() and #jwtDecode()", function () {
describe("JWT encoding then decoding", function () {
context("when alg is HS256", function () {
context("when deep equal used", function () {
it("the decoding is equal when encoding then re-decoding", function () {
const decodedHeader = { typ: "JWT", alg: "HS256" };
const decodedPayload = {
"iss": "joe",
"exp": 1300819380,
"http://example.com/is_root": true,
};
const passphrase =
"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow";

const expectedJWT =
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.lliDzOlRAdGUCfCHCPx_uisb6ZfZ1LRQa0OJLeYTTpY";
const encoded = jwtEncode(decodedHeader, decodedPayload, passphrase);

expect(encoded).to.equal(expectedJWT);

const headerPayload = createHeaderPayload(
decodedHeader,
decodedPayload
);
const sig = hs256Sign(headerPayload, passphrase);
const objOutput = {
header: decodedHeader,
payload: decodedPayload,
signature: sig,
};
const decoded = jwtDecode(encoded);
expect(decoded).to.deep.equal(objOutput);
});
});
});
});

describe("JWT decoding then encoding", function () {
context("when alg is HS256", function () {
context("when deep equal used", function () {
it("the encoding is equal when decoding then re-encoding", function () {
const decodedHeader = { typ: "JWT", alg: "HS256" };
const decodedPayload = {
"iss": "joe",
"exp": 1300819380,
"http://example.com/is_root": true,
};
const passphrase =
"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow";

const encodedJWT =
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLCJleHAiOjEzMDA4MTkzODAsImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.lliDzOlRAdGUCfCHCPx_uisb6ZfZ1LRQa0OJLeYTTpY";
const decoded = jwtDecode(encodedJWT);

const headerPayload = createHeaderPayload(
decodedHeader,
decodedPayload
);
const sig = hs256Sign(headerPayload, passphrase);
const objOutput = {
header: decodedHeader,
payload: decodedPayload,
signature: sig,
};
expect(decoded).to.deep.equal(objOutput);

const encoded = jwtEncode(
objOutput.header,
objOutput.payload,
passphrase
);

expect(encoded).to.equal(encodedJWT);
});
});
});
});
});
212 changes: 212 additions & 0 deletions test/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import { base64URLEncode } from "../src/index.js";
import crypto from "crypto";
import cli, { HELP_TEXT } from "../cli/index.js";
import sinon from "sinon";
import { expect } from "./index.js";

describe("#cli()", function () {
let sandbox;
before(function () {
sandbox = sinon.createSandbox();
});
beforeEach(function () {
sandbox.restore();
});

describe("using help", function () {
context("when argument is -h", function () {
it("shows the help screen", function () {
const log = sandbox.spy(console, "log");
// Mock process.argv
const processArgv = [0, 0, "-h"];
cli(null, [0, 0, "-h"]);
expect(log.calledOnceWith(HELP_TEXT)).to.be.true;
});
});

context("when argument is --help", function () {
it("shows the help screen", function () {
const log = sandbox.spy(console, "log");
// Mock process.argv
const processArgv = [0, 0, "-h"];
cli(null, [0, 0, "-h"]);
expect(log.calledOnceWith(HELP_TEXT)).to.be.true;
});
});
});

describe("mock using clipboard", function () {
context("when 'clipboard' contains a jwt", function () {
it("decodes the jwt", async function () {
const log = sandbox.spy(console, "log");
const myCli = cli;
const spy = sandbox.spy(myCli);

const clipboard =
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
const expectedOutput = {
header: { alg: "HS256", typ: "JWT" },
payload: { sub: "1234567890", name: "John Doe", iat: 1516239022 },
signature: "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
};
const cliOutput = await spy(clipboard, null);

expect(
spy.calledOnceWith(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
)
).to.be.true;
expect(log.calledWith("Decoding: ")).to.be.true;
expect(
log.calledWith(
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
)
).to.be.true;
expect(
log.getCall(2).firstArg,
"decoded jwt was logged to the console"
).to.be.deep.equal(expectedOutput);
expect(expectedOutput).to.be.deep.equal(cliOutput);
});
});

context("when 'clipboard' doesn't contain a jwt", function () {
context("when it's not an actual JWT", function () {
it("throws a SyntaxError", async function () {
const err = sandbox.spy(console, "error");
const clipboard = "abc.abc.abc";
let syntaxErr;

await cli(clipboard, null);
expect(err.called).to.be.true;

expect(err.calledWith("I found an error :(")).to.be.true;
});
});

context("when it doesn't contain a '.'", function () {
it("logs an error: \"Need at least one '.'\"", async function () {
const err = sandbox.spy(console, "error");
const spy = sandbox.spy(cli);
const clipboard = "abc";

await spy(clipboard, null);

expect(spy.called).to.be.true;
expect(err.calledWith(" ", "Need at least one '.'")).to.be.true;
});
});
});
});

describe("#base64urlEncode()", function () {
context("when first given argument is -b", function () {
it("properly base64url encodes the input", async function () {
const log = sandbox.spy(console, "log");
const spy = sandbox.spy(cli);

const b64u =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
const rndBytes = crypto.randomBytes(128);
const rndBytesHexString = rndBytes.toString("hex");

// Mock process.argv
const processArgv = [0, 0, "-b", rndBytesHexString];

// call the command line function. it's an async function so wait for
// result.
const cliOut = await spy(null, processArgv);

const base64urlified = base64URLEncode(rndBytesHexString);
const bufferTransform = Buffer.from(
rndBytesHexString,
"ascii"
).toString("base64url");

// Only called cli function once
expect(spy.calledOnce, "cli function should be called once").to.be.true;
expect(
spy.calledOnceWith(null, processArgv),
"cli should be called with -b and a string: " + processArgv
).to.be.true;

// Expect return value of cli function to be equal to using Buffer's transformation
expect(
cliOut,
"cli output is equal to Buffer's conversion to a base64url string"
).to.be.equal(bufferTransform);

// Expect return value of cli function to be equal to directly calling base64URLEncode
expect(
cliOut,
"cli output is equal to using the base64URLEncode function"
).to.be.equal(base64urlified);

// Cli outputs to console
expect(
log.calledOnceWith(cliOut),
"the encoded string was output to the console"
).to.be.true;

// Make sure it's not just base64 encoded
expect(cliOut.includes("+"), "no + symbols").to.be.false;
expect(cliOut.includes("/"), "no / symbols").to.be.false;

// Make sure all characters are part of the base64url char set
for (let i = 0; i < cliOut.length; i++) {
expect(b64u.includes(cliOut.charAt(i))).to.be.true;
}
});
});

context("when first given argument is --base64url", function () {
it("properly base64url encodes the input", async function () {
const log = sandbox.spy(console, "log");
const spy = sandbox.spy(cli);

const b64u =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
const rndBytes = crypto.randomBytes(128);
const rndBytesHexString = rndBytes.toString("hex");

// Mock process.argv
const processArgv = [0, 0, "-b", rndBytesHexString];

// call the command line function. it's an async function so wait for
// result.
const cliOut = await spy(null, processArgv);

const base64urlified = base64URLEncode(rndBytesHexString);
const bufferTransform = Buffer.from(
rndBytesHexString,
"ascii"
).toString("base64url");

expect(spy.calledOnce, "cli function should be called once").to.be.true;
expect(
spy.calledWith(null, processArgv),
"cli should be called with -b and a string: " + processArgv
).to.be.true;
expect(
cliOut,
"cli output is equal to Buffer's conversion to a base64url string"
).to.be.equal(bufferTransform);
expect(
cliOut,
"cli output is equal to using the base64URLEncode function"
).to.be.equal(base64urlified);
expect(
log.calledWith(cliOut),
"the encoded string was output to the console"
).to.be.true;
expect(cliOut.includes("+"), "no + symbols").to.be.false;
expect(cliOut.includes("/"), "no / symbols").to.be.false;

// Make sure all characters are part of the base64url char set
for (let i = 0; i < cliOut.length; i++) {
expect(b64u.includes(cliOut.charAt(i))).to.be.true;
}
});
});
});
});
Loading

0 comments on commit b7e8c31

Please sign in to comment.