@soapjs/soap-auth 0.4.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +261 -128
  2. package/build/index.d.ts +1 -0
  3. package/build/index.js +1 -0
  4. package/build/recipes/auth-config.recipes.d.ts +40 -0
  5. package/build/recipes/auth-config.recipes.js +135 -0
  6. package/build/recipes/http-context.helpers.d.ts +13 -0
  7. package/build/recipes/http-context.helpers.js +64 -0
  8. package/build/recipes/index.d.ts +3 -0
  9. package/build/recipes/index.js +19 -0
  10. package/build/recipes/oauth2-presets.d.ts +20 -0
  11. package/build/recipes/oauth2-presets.js +74 -0
  12. package/build/soap-auth.js +62 -0
  13. package/build/strategies/jwt/jwt.strategy.js +6 -3
  14. package/build/strategies/jwt/jwt.tools.js +8 -6
  15. package/build/strategies/local/local.strategy.d.ts +2 -2
  16. package/build/strategies/local/local.strategy.js +7 -7
  17. package/build/strategies/oauth2/hybrid.oauth2.strategy.js +1 -1
  18. package/build/strategies/oauth2/oauth2.strategy.js +2 -2
  19. package/build/strategies/oauth2/oauth2.types.d.ts +5 -0
  20. package/build/strategies/oauth2/providers/configurable-hybrid-oauth2.strategy.d.ts +19 -0
  21. package/build/strategies/oauth2/providers/configurable-hybrid-oauth2.strategy.js +85 -0
  22. package/build/strategies/oauth2/providers/configurable-oauth2.strategy.d.ts +11 -0
  23. package/build/strategies/oauth2/providers/configurable-oauth2.strategy.js +46 -0
  24. package/build/strategies/oauth2/providers/index.d.ts +2 -0
  25. package/build/strategies/oauth2/providers/index.js +2 -0
  26. package/build/strategies/oauth2/providers/provider.types.d.ts +3 -0
  27. package/build/types.d.ts +5 -2
  28. package/package.json +91 -13
  29. package/.claude/settings.local.json +0 -20
  30. package/build/__tests__/soap-auth.test.d.ts +0 -1
  31. package/build/__tests__/soap-auth.test.js +0 -136
  32. package/build/services/__tests__/account-lock.service.test.d.ts +0 -1
  33. package/build/services/__tests__/account-lock.service.test.js +0 -55
  34. package/build/services/__tests__/auth-throttle.service.test.d.ts +0 -1
  35. package/build/services/__tests__/auth-throttle.service.test.js +0 -48
  36. package/build/services/__tests__/jwks.service.test.d.ts +0 -1
  37. package/build/services/__tests__/jwks.service.test.js +0 -39
  38. package/build/services/__tests__/mfa.service.test.d.ts +0 -1
  39. package/build/services/__tests__/mfa.service.test.js +0 -66
  40. package/build/services/__tests__/password.service.test.d.ts +0 -1
  41. package/build/services/__tests__/password.service.test.js +0 -73
  42. package/build/services/__tests__/pkce.service.test.d.ts +0 -1
  43. package/build/services/__tests__/pkce.service.test.js +0 -77
  44. package/build/services/__tests__/rate-limit.service.test.d.ts +0 -1
  45. package/build/services/__tests__/rate-limit.service.test.js +0 -37
  46. package/build/services/__tests__/role.service.test.d.ts +0 -1
  47. package/build/services/__tests__/role.service.test.js +0 -31
  48. package/build/services/__tests__/totp.service.test.d.ts +0 -1
  49. package/build/services/__tests__/totp.service.test.js +0 -120
  50. package/build/session/__tests__/file.session-store.test.d.ts +0 -1
  51. package/build/session/__tests__/file.session-store.test.js +0 -117
  52. package/build/session/__tests__/memory.session-store.test.d.ts +0 -1
  53. package/build/session/__tests__/memory.session-store.test.js +0 -77
  54. package/build/session/__tests__/session-handler.test.d.ts +0 -1
  55. package/build/session/__tests__/session-handler.test.js +0 -345
  56. package/build/strategies/__tests__/base-auth.strategy.test.d.ts +0 -15
  57. package/build/strategies/__tests__/base-auth.strategy.test.js +0 -138
  58. package/build/strategies/__tests__/credential-auth.strategy.test.d.ts +0 -15
  59. package/build/strategies/__tests__/credential-auth.strategy.test.js +0 -266
  60. package/build/strategies/__tests__/token-auth.strategy.test.d.ts +0 -29
  61. package/build/strategies/__tests__/token-auth.strategy.test.js +0 -299
  62. package/build/strategies/api-key/__tests__/api-key.strategy.test.d.ts +0 -1
  63. package/build/strategies/api-key/__tests__/api-key.strategy.test.js +0 -103
  64. package/build/strategies/basic/__tests__/basic.strategy.test.d.ts +0 -1
  65. package/build/strategies/basic/__tests__/basic.strategy.test.js +0 -104
  66. package/build/strategies/jwt/__tests__/jwt.strategy.test.d.ts +0 -1
  67. package/build/strategies/jwt/__tests__/jwt.strategy.test.js +0 -156
  68. package/build/strategies/jwt/__tests__/jwt.tools.test.d.ts +0 -1
  69. package/build/strategies/jwt/__tests__/jwt.tools.test.js +0 -98
  70. package/build/strategies/local/__tests__/local.strategy.test.d.ts +0 -1
  71. package/build/strategies/local/__tests__/local.strategy.test.js +0 -121
  72. package/build/strategies/oauth2/__tests__/oauth2.strategy.test.d.ts +0 -1
  73. package/build/strategies/oauth2/__tests__/oauth2.strategy.test.js +0 -239
  74. package/build/strategies/oauth2/providers/__tests__/social-providers.test.d.ts +0 -1
  75. package/build/strategies/oauth2/providers/__tests__/social-providers.test.js +0 -201
  76. package/build/utils/__tests__/validation.test.d.ts +0 -1
  77. package/build/utils/__tests__/validation.test.js +0 -181
  78. package/jest.config.unit.json +0 -10
@@ -1,77 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const pkce_service_1 = require("../pkce.service");
4
- class InMemoryPKCEPersistence {
5
- storeMap = new Map();
6
- async store(key, meta) {
7
- this.storeMap.set(key, meta || {});
8
- }
9
- async read(verifierOrChallenge) {
10
- return this.storeMap.get(verifierOrChallenge);
11
- }
12
- async remove(key) {
13
- this.storeMap.delete(key);
14
- }
15
- }
16
- describe("PKCEService", () => {
17
- let service;
18
- let config;
19
- beforeEach(() => {
20
- config = {
21
- verifier: {
22
- expiresIn: 1,
23
- embed: jest.fn(),
24
- extract: jest.fn().mockImplementation((ctx) => ctx.verifier),
25
- persistence: new InMemoryPKCEPersistence(),
26
- },
27
- challenge: {
28
- expiresIn: 1,
29
- embed: jest.fn(),
30
- extract: jest.fn().mockImplementation((ctx) => ctx.challenge),
31
- persistence: new InMemoryPKCEPersistence(),
32
- },
33
- };
34
- service = new pkce_service_1.PKCEService(config);
35
- });
36
- it("should generate and store a code verifier", async () => {
37
- const context = { key: "code_verifier_test" };
38
- const verifier = await service.generateCodeVerifier(context);
39
- expect(verifier).toBeDefined();
40
- expect(config.verifier.embed).toHaveBeenCalledWith(context, verifier);
41
- });
42
- it("should generate and store a code challenge", async () => {
43
- const context = { key: "code_challenge_test" };
44
- const challenge = await service.generateCodeChallenge("test_verifier", context);
45
- expect(challenge).toBeDefined();
46
- expect(config.challenge.embed).toHaveBeenCalledWith(context, challenge);
47
- });
48
- it("should detect expired code verifier", async () => {
49
- config.verifier.embed = (ctx, cv) => {
50
- ctx.verifier = cv;
51
- };
52
- const context = { key: "expired_verifier_test" };
53
- await service.generateCodeVerifier(context);
54
- await new Promise((resolve) => setTimeout(resolve, 1500));
55
- const isExpired = await service.isCodeVerifierExpired(context);
56
- expect(isExpired).toBe(true);
57
- });
58
- it("should detect not-expired code verifier", async () => {
59
- config.verifier.embed = (ctx, cv) => {
60
- ctx.verifier = cv;
61
- };
62
- const context = {
63
- key: "not_expired_verifier_test",
64
- };
65
- await service.generateCodeVerifier(context);
66
- await new Promise((resolve) => setTimeout(resolve, 500));
67
- const isExpired = await service.isCodeVerifierExpired(context);
68
- expect(isExpired).toBe(false);
69
- });
70
- it("should clear code verifier", async () => {
71
- const context = {
72
- key: "clear_verifier_test",
73
- verifier: "test_verifier_value",
74
- };
75
- await service.clearCodeVerifier(context);
76
- });
77
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,37 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const errors_1 = require("../../errors");
4
- const rate_limit_service_1 = require("../rate-limit.service");
5
- const mockConfig = {
6
- incrementRequestCount: jest.fn(),
7
- checkRateLimit: jest.fn(),
8
- };
9
- const mockLogger = {
10
- error: jest.fn(),
11
- };
12
- describe("RateLimitService", () => {
13
- let service;
14
- const mockData = { userId: "user123" };
15
- beforeEach(() => {
16
- jest.clearAllMocks();
17
- service = new rate_limit_service_1.RateLimitService(mockConfig, mockLogger);
18
- });
19
- it("incrementRequestCount calls config method and handles errors", async () => {
20
- mockConfig.incrementRequestCount.mockResolvedValue(undefined);
21
- await expect(service.incrementRequestCount(mockData)).resolves.not.toThrow();
22
- expect(mockConfig.incrementRequestCount).toHaveBeenCalledWith(mockData);
23
- });
24
- it("incrementRequestCount logs error if an exception occurs", async () => {
25
- mockConfig.incrementRequestCount.mockRejectedValue(new Error("Increment failed"));
26
- await expect(service.incrementRequestCount(mockData)).resolves.not.toThrow();
27
- expect(mockLogger.error).toHaveBeenCalledWith(expect.any(Error));
28
- });
29
- it("checkRateLimit throws RateLimitExceededError if limit is exceeded", async () => {
30
- mockConfig.checkRateLimit.mockResolvedValue(true);
31
- await expect(service.checkRateLimit(mockData)).rejects.toThrow(errors_1.RateLimitExceededError);
32
- });
33
- it("checkRateLimit does not throw error if limit is not exceeded", async () => {
34
- mockConfig.checkRateLimit.mockResolvedValue(false);
35
- await expect(service.checkRateLimit(mockData)).resolves.not.toThrow();
36
- });
37
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,31 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const errors_1 = require("../../errors");
4
- const role_service_1 = require("../role.service");
5
- const mockConfig = {
6
- authorizeByRoles: jest.fn(),
7
- roles: ["admin", "editor"],
8
- };
9
- const mockLogger = {
10
- error: jest.fn(),
11
- };
12
- describe("RoleService", () => {
13
- let service;
14
- const mockUser = { id: "user123", role: "viewer" };
15
- beforeEach(() => {
16
- jest.clearAllMocks();
17
- service = new role_service_1.RoleService(mockConfig, mockLogger);
18
- });
19
- it("isAuthorized throws UnauthorizedRoleError if user is not authorized", async () => {
20
- mockConfig.authorizeByRoles.mockResolvedValue(false);
21
- await expect(service.isAuthorized(mockUser)).rejects.toThrow(errors_1.UnauthorizedRoleError);
22
- });
23
- it("isAuthorized returns true if user is authorized", async () => {
24
- mockConfig.authorizeByRoles.mockResolvedValue(true);
25
- await expect(service.isAuthorized(mockUser)).resolves.toBe(true);
26
- });
27
- it("isAuthorized bypasses role check if authorizeByRoles is not defined", async () => {
28
- service = new role_service_1.RoleService({ roles: [] }, mockLogger);
29
- await expect(service.isAuthorized(mockUser)).resolves.toBe(true);
30
- });
31
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,120 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const totp_service_1 = require("../totp.service");
4
- describe("TotpService", () => {
5
- const totp = new totp_service_1.TotpService();
6
- describe("generateSecret", () => {
7
- it("returns a non-empty base32 string", () => {
8
- const secret = totp.generateSecret();
9
- expect(secret).toMatch(/^[A-Z2-7]+$/);
10
- expect(secret.length).toBeGreaterThan(0);
11
- });
12
- it("returns unique secrets", () => {
13
- const a = totp.generateSecret();
14
- const b = totp.generateSecret();
15
- expect(a).not.toBe(b);
16
- });
17
- });
18
- describe("generateOTP", () => {
19
- it("returns a 6-digit string for a fixed timestamp", () => {
20
- const secret = totp.generateSecret();
21
- const code = totp.generateOTP(secret, 1000000000000);
22
- expect(code).toMatch(/^\d{6}$/);
23
- });
24
- it("returns the same code within the same time step", () => {
25
- const secret = totp.generateSecret();
26
- const ts = 1000000000000;
27
- expect(totp.generateOTP(secret, ts)).toBe(totp.generateOTP(secret, ts + 5000));
28
- });
29
- it("returns a different code in a different time step", () => {
30
- const secret = totp.generateSecret();
31
- const ts = 1000000000000;
32
- const code1 = totp.generateOTP(secret, ts);
33
- const code2 = totp.generateOTP(secret, ts + 30_000);
34
- expect(code1).not.toBe(code2);
35
- });
36
- });
37
- describe("verifyOTP", () => {
38
- it("accepts a valid code at the current timestamp", () => {
39
- const secret = totp.generateSecret();
40
- const ts = Date.now();
41
- const code = totp.generateOTP(secret, ts);
42
- expect(totp.verifyOTP(secret, code, ts)).toBe(true);
43
- });
44
- it("accepts a code one step in the past (window = 1)", () => {
45
- const secret = totp.generateSecret();
46
- const ts = 1000000000000;
47
- const pastCode = totp.generateOTP(secret, ts - 30_000);
48
- expect(totp.verifyOTP(secret, pastCode, ts)).toBe(true);
49
- });
50
- it("accepts a code one step in the future (window = 1)", () => {
51
- const secret = totp.generateSecret();
52
- const ts = 1000000000000;
53
- const futureCode = totp.generateOTP(secret, ts + 30_000);
54
- expect(totp.verifyOTP(secret, futureCode, ts)).toBe(true);
55
- });
56
- it("rejects a code two steps old when window = 1", () => {
57
- const secret = totp.generateSecret();
58
- const ts = 1000000000000;
59
- const oldCode = totp.generateOTP(secret, ts - 60_000);
60
- expect(totp.verifyOTP(secret, oldCode, ts)).toBe(false);
61
- });
62
- it("rejects an invalid code", () => {
63
- const secret = totp.generateSecret();
64
- expect(totp.verifyOTP(secret, "000000", Date.now())).toBe(false);
65
- });
66
- it("rejects non-numeric input", () => {
67
- const secret = totp.generateSecret();
68
- expect(totp.verifyOTP(secret, "abcdef")).toBe(false);
69
- });
70
- it("rejects wrong-length input", () => {
71
- const secret = totp.generateSecret();
72
- expect(totp.verifyOTP(secret, "12345")).toBe(false);
73
- });
74
- });
75
- describe("custom options", () => {
76
- it("generates 8-digit codes when configured", () => {
77
- const t8 = new totp_service_1.TotpService({ digits: 8 });
78
- const secret = t8.generateSecret();
79
- const code = t8.generateOTP(secret);
80
- expect(code).toMatch(/^\d{8}$/);
81
- });
82
- it("accepts window = 0 — only the current step", () => {
83
- const t0 = new totp_service_1.TotpService({ window: 0 });
84
- const secret = t0.generateSecret();
85
- const ts = 1000000000000;
86
- const pastCode = t0.generateOTP(secret, ts - 30_000);
87
- expect(t0.verifyOTP(secret, pastCode, ts)).toBe(false);
88
- const current = t0.generateOTP(secret, ts);
89
- expect(t0.verifyOTP(secret, current, ts)).toBe(true);
90
- });
91
- });
92
- describe("generateQRUri", () => {
93
- it("returns a valid otpauth URI", () => {
94
- const secret = totp.generateSecret();
95
- const uri = totp.generateQRUri(secret, "MyApp", "user@example.com");
96
- expect(uri).toMatch(/^otpauth:\/\/totp\//);
97
- expect(uri).toContain(secret);
98
- expect(uri).toContain("issuer=MyApp");
99
- });
100
- });
101
- describe("generateBackupCodes", () => {
102
- it("returns 8 codes by default", () => {
103
- const codes = totp.generateBackupCodes();
104
- expect(codes).toHaveLength(8);
105
- });
106
- it("returns the requested count", () => {
107
- const codes = totp.generateBackupCodes(10);
108
- expect(codes).toHaveLength(10);
109
- });
110
- it("returns uppercase hex strings of 12 chars", () => {
111
- const codes = totp.generateBackupCodes();
112
- codes.forEach((c) => expect(c).toMatch(/^[0-9A-F]{12}$/));
113
- });
114
- it("returns unique codes", () => {
115
- const codes = totp.generateBackupCodes(20);
116
- const unique = new Set(codes);
117
- expect(unique.size).toBe(20);
118
- });
119
- });
120
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,117 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const file_session_store_1 = require("../file.session-store");
7
- const promises_1 = __importDefault(require("fs/promises"));
8
- const path_1 = __importDefault(require("path"));
9
- jest.mock("fs/promises", () => ({
10
- mkdir: jest.fn(),
11
- readFile: jest.fn(),
12
- writeFile: jest.fn(),
13
- unlink: jest.fn(),
14
- readdir: jest.fn(),
15
- }));
16
- describe("FileSessionStore", () => {
17
- let store;
18
- const mockSessionsDir = "/mock/sessions";
19
- beforeEach(() => {
20
- store = new file_session_store_1.FileSessionStore(mockSessionsDir);
21
- jest.clearAllMocks();
22
- });
23
- describe("init", () => {
24
- it("should create the sessions directory recursively", async () => {
25
- await store.init();
26
- expect(promises_1.default.mkdir).toHaveBeenCalledWith(mockSessionsDir, {
27
- recursive: true,
28
- });
29
- });
30
- it("should catch and log errors if mkdir fails", async () => {
31
- const consoleSpy = jest
32
- .spyOn(console, "error")
33
- .mockImplementation(() => { });
34
- promises_1.default.mkdir.mockRejectedValueOnce(new Error("mkdir error"));
35
- await store.init();
36
- expect(consoleSpy).toHaveBeenCalledWith("Error initializing session directory:", expect.any(Error));
37
- consoleSpy.mockRestore();
38
- });
39
- });
40
- describe("getSession", () => {
41
- it("should return parsed JSON if file read is successful", async () => {
42
- const mockData = { user: { id: "123" } };
43
- promises_1.default.readFile.mockResolvedValueOnce(JSON.stringify(mockData));
44
- const session = await store.getSession("session123");
45
- expect(session).toEqual(mockData);
46
- expect(promises_1.default.readFile).toHaveBeenCalledWith(path_1.default.join(mockSessionsDir, "session123"), "utf8");
47
- });
48
- it("should return null and log error if readFile fails", async () => {
49
- const consoleSpy = jest
50
- .spyOn(console, "error")
51
- .mockImplementation(() => { });
52
- promises_1.default.readFile.mockRejectedValueOnce(new Error("read error"));
53
- const session = await store.getSession("badSession");
54
- expect(session).toBeNull();
55
- expect(consoleSpy).toHaveBeenCalledWith("Error getting session:", expect.any(Error));
56
- consoleSpy.mockRestore();
57
- });
58
- it("should return null if JSON parse fails", async () => {
59
- const consoleSpy = jest
60
- .spyOn(console, "error")
61
- .mockImplementation(() => { });
62
- promises_1.default.readFile.mockResolvedValueOnce("invalid-json");
63
- const session = await store.getSession("corruptSession");
64
- expect(session).toBeNull();
65
- expect(consoleSpy).toHaveBeenCalledWith("Error getting session:", expect.any(Error));
66
- consoleSpy.mockRestore();
67
- });
68
- });
69
- describe("setSession", () => {
70
- it("should write stringified session data to a file", async () => {
71
- const mockData = { user: { id: "xyz" } };
72
- await store.setSession("sessionABC", mockData);
73
- expect(promises_1.default.writeFile).toHaveBeenCalledWith(path_1.default.join(mockSessionsDir, "sessionABC"), JSON.stringify(mockData), "utf8");
74
- });
75
- it("should throw error if writeFile fails", async () => {
76
- promises_1.default.writeFile.mockRejectedValueOnce(new Error("write error"));
77
- await expect(store.setSession("sessionError", { user: { id: "1" } })).rejects.toThrow("write error");
78
- });
79
- });
80
- describe("destroySession", () => {
81
- it("should unlink the file associated with the session ID", async () => {
82
- await store.destroySession("destroyMe");
83
- expect(promises_1.default.unlink).toHaveBeenCalledWith(path_1.default.join(mockSessionsDir, "destroyMe"));
84
- });
85
- it("should ignore ENOENT errors (file not found)", async () => {
86
- promises_1.default.unlink.mockRejectedValueOnce({ code: "ENOENT" });
87
- await expect(store.destroySession("notExists")).resolves.toBeUndefined();
88
- });
89
- it("should rethrow other errors", async () => {
90
- promises_1.default.unlink.mockRejectedValueOnce(new Error("unlink error"));
91
- await expect(store.destroySession("sessionX")).rejects.toThrow("unlink error");
92
- });
93
- });
94
- describe("touchSession", () => {
95
- it("should call setSession with updated data", async () => {
96
- const setSessionSpy = jest.spyOn(store, "setSession").mockResolvedValue();
97
- const newData = { user: { id: "touchMe" } };
98
- await store.touchSession("touchId", newData);
99
- expect(setSessionSpy).toHaveBeenCalledWith("touchId", newData);
100
- });
101
- });
102
- describe("getSessionIds", () => {
103
- it("should return the base names of files in the sessions directory", async () => {
104
- promises_1.default.readdir.mockResolvedValueOnce([
105
- { name: "abc", isFile: () => true },
106
- { name: "def.json", isFile: () => true },
107
- { name: "subdir", isFile: () => false },
108
- ]);
109
- const result = await store.getSessionIds();
110
- expect(result).toEqual(["abc", "def"]);
111
- });
112
- it("should throw if readdir fails", async () => {
113
- promises_1.default.readdir.mockRejectedValueOnce(new Error("readdir error"));
114
- await expect(store.getSessionIds()).rejects.toThrow("readdir error");
115
- });
116
- });
117
- });
@@ -1,77 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const memory_session_store_1 = require("../memory.session-store");
4
- describe("MemorySessionStore", () => {
5
- let store;
6
- beforeEach(() => {
7
- store = new memory_session_store_1.MemorySessionStore();
8
- });
9
- describe("getSession", () => {
10
- it("should return null if session does not exist", async () => {
11
- const data = await store.getSession("nonExistent");
12
- expect(data).toBeNull();
13
- });
14
- it("should return the stored session data if present", async () => {
15
- await store.setSession("session1", {
16
- user: { id: "123", name: "John" },
17
- });
18
- const session = await store.getSession("session1");
19
- expect(session).toEqual({ user: { id: "123", name: "John" } });
20
- });
21
- });
22
- describe("setSession", () => {
23
- it("should store session data in memory", async () => {
24
- await store.setSession("session2", {
25
- user: { id: "abc", name: "Alice" },
26
- });
27
- const session = await store.getSession("session2");
28
- expect(session).toEqual({ user: { id: "abc", name: "Alice" } });
29
- });
30
- });
31
- describe("destroySession", () => {
32
- it("should delete the session from memory", async () => {
33
- await store.setSession("sessionDelete", {
34
- user: { id: "xyz", name: "Jane" },
35
- });
36
- await store.destroySession("sessionDelete");
37
- const session = await store.getSession("sessionDelete");
38
- expect(session).toBeNull();
39
- });
40
- it("should not throw if the session does not exist", async () => {
41
- await expect(store.destroySession("noSession")).resolves.not.toThrow();
42
- });
43
- });
44
- describe("touchSession", () => {
45
- it("should update the session data if it exists", async () => {
46
- await store.setSession("sessionTouch", {
47
- user: { id: "initial", name: "Old" },
48
- });
49
- await store.touchSession("sessionTouch", {
50
- user: { id: "updated", name: "New" },
51
- });
52
- const updated = await store.getSession("sessionTouch");
53
- expect(updated).toEqual({ user: { id: "updated", name: "New" } });
54
- });
55
- it("should create a new session if it doesn't exist (similar to setSession)", async () => {
56
- await store.touchSession("sessionNew", {
57
- user: { id: "newId", name: "New Name" },
58
- });
59
- const data = await store.getSession("sessionNew");
60
- expect(data).toEqual({ user: { id: "newId", name: "New Name" } });
61
- });
62
- });
63
- describe("getSessionIds", () => {
64
- it("should return an empty array if no sessions exist", async () => {
65
- const ids = await store.getSessionIds();
66
- expect(ids).toEqual([]);
67
- });
68
- it("should return all session IDs currently in memory", async () => {
69
- await store.setSession("id1", { user: { id: "one" } });
70
- await store.setSession("id2", { user: { id: "two" } });
71
- const ids = await store.getSessionIds();
72
- expect(ids).toContain("id1");
73
- expect(ids).toContain("id2");
74
- expect(ids.length).toBe(2);
75
- });
76
- });
77
- });
@@ -1 +0,0 @@
1
- export {};