-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: auth cookie serialization/deserialization (#93)
* fix: auth cookie serialization/deserialization * format * add suffix * format * fix connection loop * chore: add test coverage to the new issues * chore: bump version * chore: PR comments * feat: use real cookies for testing * chore: restore timer --------- Co-authored-by: Alexandru Ciobanu <alex+git@ciobanu.org>
- Loading branch information
1 parent
bb79b37
commit 60be189
Showing
6 changed files
with
252 additions
and
60 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,82 +1,236 @@ | ||
import Cookies from "js-cookie"; | ||
import { describe, expect, test, vi } from "vitest"; | ||
import { | ||
afterAll, | ||
afterEach, | ||
beforeAll, | ||
describe, | ||
expect, | ||
test, | ||
vi, | ||
} from "vitest"; | ||
|
||
import { | ||
checkPromptMessageCompleted, | ||
forgetAuthToken, | ||
getAuthToken, | ||
markPromptMessageCompleted, | ||
rememberAuthToken, | ||
} from "../src/prompt-storage"; | ||
|
||
vi.mock("js-cookie"); | ||
|
||
describe("prompt-storage", () => { | ||
test("markPromptMessageCompleted", async () => { | ||
const spy = vi.spyOn(Cookies, "set"); | ||
beforeAll(() => { | ||
const cookies: Record<string, string> = {}; | ||
|
||
Object.defineProperty(document, "cookie", { | ||
set: (val: string) => { | ||
if (!val) { | ||
Object.keys(cookies).forEach((k) => delete cookies[k]); | ||
return; | ||
} | ||
const i = val.indexOf("="); | ||
cookies[val.slice(0, i)] = val.slice(i + 1); | ||
}, | ||
get: () => | ||
Object.entries(cookies) | ||
.map(([k, v]) => `${k}=${v}`) | ||
.join("; "), | ||
}); | ||
|
||
markPromptMessageCompleted("user", "prompt", new Date("2021-01-01")); | ||
vi.setSystemTime(new Date("2024-01-11T09:55:37.000Z")); | ||
}); | ||
|
||
expect(spy).toHaveBeenCalledWith("bucket-prompt-user", "prompt", { | ||
expires: new Date("2021-01-01"), | ||
sameSite: "strict", | ||
secure: true, | ||
}); | ||
afterEach(() => { | ||
document.cookie = undefined!; | ||
vi.clearAllMocks(); | ||
}); | ||
|
||
test("checkPromptMessageCompleted with positive result", async () => { | ||
const spy = vi.spyOn(Cookies, "get").mockReturnValue("prompt" as any); | ||
afterAll(() => { | ||
vi.useRealTimers(); | ||
}); | ||
|
||
expect(checkPromptMessageCompleted("user", "prompt")).toBe(true); | ||
describe("markPromptMessageCompleted", () => { | ||
test("adds new cookie", async () => { | ||
markPromptMessageCompleted( | ||
"user", | ||
"prompt2", | ||
new Date("2024-01-04T14:01:20.000Z"), | ||
); | ||
|
||
expect(document.cookie).toBe( | ||
"bucket-prompt-user=prompt2; path=/; expires=Thu, 04 Jan 2024 14:01:20 GMT; sameSite=strict; secure", | ||
); | ||
}); | ||
|
||
test("rewrites existing cookie", async () => { | ||
document.cookie = | ||
"bucket-prompt-user=prompt1; path=/; expires=Thu, 04 Jan 2021 14:01:20 GMT; sameSite=strict; secure"; | ||
|
||
markPromptMessageCompleted( | ||
"user", | ||
"prompt2", | ||
new Date("2024-01-04T14:01:20.000Z"), | ||
); | ||
|
||
expect(spy).toHaveBeenCalledWith("bucket-prompt-user"); | ||
expect(document.cookie).toBe( | ||
"bucket-prompt-user=prompt2; path=/; expires=Thu, 04 Jan 2024 14:01:20 GMT; sameSite=strict; secure", | ||
); | ||
}); | ||
}); | ||
|
||
test("checkPromptMessageCompleted with negative result", async () => { | ||
const spy = vi.spyOn(Cookies, "get").mockReturnValue("other" as any); | ||
describe("checkPromptMessageCompleted", () => { | ||
test("cookie with same use and prompt results in true", async () => { | ||
document.cookie = | ||
"bucket-prompt-user=prompt; path=/; expires=Thu, 04 Jan 2024 14:01:20 GMT; sameSite=strict; secure"; | ||
|
||
expect(checkPromptMessageCompleted("user", "prompt")).toBe(false); | ||
expect(checkPromptMessageCompleted("user", "prompt")).toBe(true); | ||
|
||
expect(spy).toHaveBeenCalledWith("bucket-prompt-user"); | ||
}); | ||
expect(document.cookie).toBe( | ||
"bucket-prompt-user=prompt; path=/; expires=Thu, 04 Jan 2024 14:01:20 GMT; sameSite=strict; secure", | ||
); | ||
}); | ||
|
||
test("cookie with different prompt results in false", async () => { | ||
document.cookie = | ||
"bucket-prompt-user=prompt1; path=/; expires=Thu, 04 Jan 2024 14:01:20 GMT; sameSite=strict; secure"; | ||
|
||
test("rememberAuthToken", async () => { | ||
const spy = vi.spyOn(Cookies, "set"); | ||
expect(checkPromptMessageCompleted("user", "prompt2")).toBe(false); | ||
}); | ||
|
||
rememberAuthToken("user", "channel", "token", new Date("2021-01-01")); | ||
test("cookie with different user results in false", async () => { | ||
document.cookie = | ||
"bucket-prompt-user1=prompt1; path=/; expires=Thu, 04 Jan 2024 14:01:20 GMT; sameSite=strict; secure"; | ||
|
||
expect(checkPromptMessageCompleted("user2", "prompt1")).toBe(false); | ||
}); | ||
|
||
expect(spy).toHaveBeenCalledWith("bucket-token-user", "channel:token", { | ||
expires: new Date("2021-01-01"), | ||
sameSite: "strict", | ||
secure: true, | ||
test("no cookie results in false", async () => { | ||
expect(checkPromptMessageCompleted("user", "prompt2")).toBe(false); | ||
}); | ||
}); | ||
|
||
test("getAuthToken with positive result", async () => { | ||
const spy = vi | ||
.spyOn(Cookies, "get") | ||
.mockReturnValue("channel:token" as any); | ||
describe("rememberAuthToken", () => { | ||
test("adds new cookie if none was there", async () => { | ||
expect(document.cookie).toBe(""); | ||
|
||
expect(getAuthToken("user")).toStrictEqual({ | ||
channel: "channel", | ||
token: "token", | ||
rememberAuthToken( | ||
'user1"%%', | ||
"channel:suffix", | ||
"secret$%", | ||
new Date("2024-01-02T15:02:20.000Z"), | ||
); | ||
|
||
expect(document.cookie).toBe( | ||
"bucket-token-user1%22%25%25={%22channel%22:%22channel:suffix%22%2C%22token%22:%22secret$%25%22}; path=/; expires=Tue, 02 Jan 2024 15:02:20 GMT; sameSite=strict; secure", | ||
); | ||
}); | ||
|
||
expect(spy).toHaveBeenCalledWith("bucket-token-user"); | ||
test("replaces existing cookie for same user", async () => { | ||
document.cookie = | ||
"bucket-token-user1%22%25%25={%22channel%22:%22channel:suffix%22%2C%22token%22:%22secret$%25%22}; path=/; expires=Tue, 02 Jan 2024 15:02:20 GMT; sameSite=strict; secure"; | ||
|
||
rememberAuthToken( | ||
'user1"%%', | ||
"channel2:suffix2", | ||
"secret2$%", | ||
new Date("2023-01-02T15:02:20.000Z"), | ||
); | ||
|
||
expect(document.cookie).toBe( | ||
"bucket-token-user1%22%25%25={%22channel%22:%22channel2:suffix2%22%2C%22token%22:%22secret2$%25%22}; path=/; expires=Mon, 02 Jan 2023 15:02:20 GMT; sameSite=strict; secure", | ||
); | ||
}); | ||
}); | ||
|
||
test("getAuthToken with error", async () => { | ||
const spy = vi.spyOn(Cookies, "get").mockReturnValue("token" as any); | ||
describe("forgetAuthToken", () => { | ||
test("clears the user's cookie if even if there was nothing before", async () => { | ||
forgetAuthToken("user"); | ||
|
||
expect(getAuthToken("user")).toBeUndefined(); | ||
expect(document.cookie).toBe( | ||
"bucket-token-user=; path=/; expires=Wed, 10 Jan 2024 09:55:37 GMT", | ||
); | ||
}); | ||
|
||
expect(spy).toHaveBeenCalledWith("bucket-token-user"); | ||
test("clears the user's cookie", async () => { | ||
document.cookie = | ||
"bucket-token-user1={%22channel%22:%22channel:suffix%22%2C%22token%22:%22secret$%25%22}; path=/; expires=Tue, 02 Jan 2024 15:02:20 GMT; sameSite=strict; secure"; | ||
|
||
forgetAuthToken("user1"); | ||
|
||
expect(document.cookie).toBe( | ||
"bucket-token-user1=; path=/; expires=Wed, 10 Jan 2024 09:55:37 GMT", | ||
); | ||
}); | ||
|
||
test("does nothing if there is a cookie for a different user", async () => { | ||
document.cookie = | ||
"bucket-token-user1={%22channel%22:%22channel:suffix%22%2C%22token%22:%22secret$%25%22}; path=/; expires=Tue, 02 Jan 2028 15:02:20 GMT; sameSite=strict; secure"; | ||
|
||
forgetAuthToken("user2"); | ||
|
||
expect(document.cookie).toBe( | ||
"bucket-token-user1={%22channel%22:%22channel:suffix%22%2C%22token%22:%22secret$%25%22}; path=/; expires=Tue, 02 Jan 2028 15:02:20 GMT; sameSite=strict; secure; bucket-token-user2=; path=/; expires=Wed, 10 Jan 2024 09:55:37 GMT", | ||
); | ||
}); | ||
}); | ||
|
||
test("getAuthToken with negative result", async () => { | ||
const spy = vi.spyOn(Cookies, "get").mockReturnValue(undefined as any); | ||
describe("getAuthToken", () => { | ||
test("returns the auth token if it's available for the user", async () => { | ||
document.cookie = | ||
"bucket-token-user1%22%25%25={%22channel%22:%22channel:suffix%22%2C%22token%22:%22secret$%25%22}; path=/; expires=Tue, 02 Jan 2024 15:02:20 GMT; sameSite=strict; secure"; | ||
|
||
expect(getAuthToken('user1"%%')).toStrictEqual({ | ||
channel: "channel:suffix", | ||
token: "secret$%", | ||
}); | ||
}); | ||
|
||
test("return undefined if no cookie for user", async () => { | ||
document.cookie = | ||
"bucket-token-user1={%22channel%22:%22channel:suffix%22%2C%22token%22:%22secret$%25%22}; path=/; expires=Tue, 02 Jan 2024 15:02:20 GMT; sameSite=strict; secure"; | ||
|
||
expect(getAuthToken("user")).toBeUndefined(); | ||
expect(getAuthToken("user2")).toBeUndefined(); | ||
}); | ||
|
||
test("returns undefined if no cookie", async () => { | ||
expect(getAuthToken("user")).toBeUndefined(); | ||
}); | ||
|
||
test("return undefined if corrupted cookie", async () => { | ||
document.cookie = | ||
"bucket-token-user={channel%22:%22channel:suffix%22%2C%22token%22:%22secret$%25%22}; path=/; expires=Tue, 02 Jan 2024 15:02:20 GMT; sameSite=strict; secure"; | ||
|
||
expect(getAuthToken("user")).toBeUndefined(); | ||
}); | ||
|
||
test("return undefined if a field is missing", async () => { | ||
document.cookie = | ||
"bucket-token-user={%2C%22token%22:%22secret$%25%22}; path=/; expires=Tue, 02 Jan 2024 15:02:20 GMT; sameSite=strict; secure"; | ||
|
||
expect(getAuthToken("user")).toBeUndefined(); | ||
}); | ||
}); | ||
|
||
expect(spy).toHaveBeenCalledWith("bucket-token-user"); | ||
test("manages all cookies for the user", () => { | ||
rememberAuthToken( | ||
"user1", | ||
"channel:suffix", | ||
"secret$%", | ||
new Date("2024-01-02T15:02:20.000Z"), | ||
); | ||
|
||
markPromptMessageCompleted( | ||
"user1", | ||
"alex-prompt", | ||
new Date("2024-01-02T15:03:20.000Z"), | ||
); | ||
|
||
expect(document.cookie).toBe( | ||
"bucket-token-user1={%22channel%22:%22channel:suffix%22%2C%22token%22:%22secret$%25%22}; path=/; expires=Tue, 02 Jan 2024 15:02:20 GMT; sameSite=strict; secure; bucket-prompt-user1=alex-prompt; path=/; expires=Tue, 02 Jan 2024 15:03:20 GMT; sameSite=strict; secure", | ||
); | ||
|
||
forgetAuthToken("user1"); | ||
|
||
expect(document.cookie).toBe( | ||
"bucket-token-user1=; path=/; expires=Wed, 10 Jan 2024 09:55:37 GMT; bucket-prompt-user1=alex-prompt; path=/; expires=Tue, 02 Jan 2024 15:03:20 GMT; sameSite=strict; secure", | ||
); | ||
}); | ||
}); |
Oops, something went wrong.