@raketa-cloud/admin 1.3.0 → 1.4.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.
- package/package.json +1 -1
- package/src/HonoAdapter.js +303 -167
- package/src/filters.js +17 -0
- package/src/index.js +1 -0
- package/src/routeCollection.js +14 -83
- package/src/routeMember.js +10 -33
- package/src/routeNestedCollection.js +19 -47
- package/src/routeNestedMember.js +14 -40
- package/src/tests/Adapter.test.js +4 -4
- package/src/tests/HonoAdapter.filter.test.js +40 -0
- package/src/tests/HonoAdapter.test.js +110 -9
- package/src/tests/RaketaContext.test.js +65 -0
- package/src/tests/cipher.test.js +73 -0
- package/src/tests/filters.test.js +97 -0
- package/src/tests/routeCollection.test.js +0 -92
- package/src/tests/routeMember.test.js +0 -6
- package/src/tests/routeNestedCollection.test.js +0 -14
- package/src/tests/routeNestedMember.test.js +0 -6
- package/src/tests/utils.test.js +70 -0
- package/src/Adapter.js +0 -265
package/src/routeCollection.js
CHANGED
|
@@ -1,89 +1,20 @@
|
|
|
1
1
|
const routeCollection = (adapter, resource, handler, resourceConfig = {}) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
show,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
destroy,
|
|
12
|
-
} = handler;
|
|
13
|
-
|
|
14
|
-
const mapping = [
|
|
15
|
-
[
|
|
16
|
-
"GET",
|
|
17
|
-
`/${resource}`,
|
|
18
|
-
resource,
|
|
19
|
-
resource,
|
|
20
|
-
"index",
|
|
21
|
-
index,
|
|
22
|
-
before_filter,
|
|
23
|
-
after_filter,
|
|
24
|
-
],
|
|
25
|
-
[
|
|
26
|
-
"GET",
|
|
27
|
-
`/${resource}/new`,
|
|
28
|
-
resource,
|
|
29
|
-
resource,
|
|
30
|
-
"create_form",
|
|
31
|
-
create_form,
|
|
32
|
-
before_filter,
|
|
33
|
-
after_filter,
|
|
34
|
-
],
|
|
35
|
-
[
|
|
36
|
-
"GET",
|
|
37
|
-
`/${resource}/:id`,
|
|
38
|
-
resource,
|
|
39
|
-
resource,
|
|
40
|
-
"show",
|
|
41
|
-
show,
|
|
42
|
-
before_filter,
|
|
43
|
-
after_filter,
|
|
44
|
-
],
|
|
45
|
-
[
|
|
46
|
-
"GET",
|
|
47
|
-
`/${resource}/:id/edit`,
|
|
48
|
-
resource,
|
|
49
|
-
resource,
|
|
50
|
-
"edit_form",
|
|
51
|
-
edit_form,
|
|
52
|
-
before_filter,
|
|
53
|
-
after_filter,
|
|
54
|
-
],
|
|
55
|
-
[
|
|
56
|
-
"POST",
|
|
57
|
-
`/${resource}`,
|
|
58
|
-
resource,
|
|
59
|
-
resource,
|
|
60
|
-
"create_form",
|
|
61
|
-
create,
|
|
62
|
-
before_filter,
|
|
63
|
-
after_filter,
|
|
64
|
-
],
|
|
65
|
-
[
|
|
66
|
-
"POST",
|
|
67
|
-
`/${resource}/:id`,
|
|
68
|
-
resource,
|
|
69
|
-
resource,
|
|
70
|
-
"edit_form",
|
|
71
|
-
update,
|
|
72
|
-
before_filter,
|
|
73
|
-
after_filter,
|
|
74
|
-
],
|
|
75
|
-
[
|
|
76
|
-
"DELETE",
|
|
77
|
-
`/${resource}/:id`,
|
|
78
|
-
resource,
|
|
79
|
-
resource,
|
|
80
|
-
"destroy",
|
|
81
|
-
destroy,
|
|
82
|
-
before_filter,
|
|
83
|
-
after_filter,
|
|
84
|
-
],
|
|
2
|
+
// Create route definitions
|
|
3
|
+
const routes = [
|
|
4
|
+
["GET", `/${resource}`, "index", handler.index],
|
|
5
|
+
["GET", `/${resource}/new`, "create_form", handler.create_form],
|
|
6
|
+
["GET", `/${resource}/:id`, "show", handler.show],
|
|
7
|
+
["GET", `/${resource}/:id/edit`, "edit_form", handler.edit_form],
|
|
8
|
+
["POST", `/${resource}`, "create_form", handler.create],
|
|
9
|
+
["POST", `/${resource}/:id`, "edit_form", handler.update],
|
|
10
|
+
["DELETE", `/${resource}/:id`, "destroy", handler.destroy]
|
|
85
11
|
];
|
|
86
12
|
|
|
13
|
+
// Map routes to full configuration
|
|
14
|
+
const mapping = routes.map(([verb, path, action, fn]) => [
|
|
15
|
+
verb, path, resource, resource, action, fn
|
|
16
|
+
]);
|
|
17
|
+
|
|
87
18
|
adapter.routeCollection(mapping, resourceConfig);
|
|
88
19
|
};
|
|
89
20
|
|
package/src/routeMember.js
CHANGED
|
@@ -1,39 +1,16 @@
|
|
|
1
1
|
const routeMember = (adapter, resource, handler, resourceConfig = {}) => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
[
|
|
6
|
-
|
|
7
|
-
`/${resource}`,
|
|
8
|
-
resource,
|
|
9
|
-
resource,
|
|
10
|
-
"show",
|
|
11
|
-
show,
|
|
12
|
-
before_filter,
|
|
13
|
-
after_filter,
|
|
14
|
-
],
|
|
15
|
-
[
|
|
16
|
-
"GET",
|
|
17
|
-
`/${resource}/edit`,
|
|
18
|
-
resource,
|
|
19
|
-
resource,
|
|
20
|
-
"edit_form",
|
|
21
|
-
edit_form,
|
|
22
|
-
before_filter,
|
|
23
|
-
after_filter,
|
|
24
|
-
],
|
|
25
|
-
[
|
|
26
|
-
"POST",
|
|
27
|
-
`/${resource}`,
|
|
28
|
-
resource,
|
|
29
|
-
resource,
|
|
30
|
-
"edit_form",
|
|
31
|
-
update,
|
|
32
|
-
before_filter,
|
|
33
|
-
after_filter,
|
|
34
|
-
],
|
|
2
|
+
// Create route definitions
|
|
3
|
+
const routes = [
|
|
4
|
+
["GET", `/${resource}`, "show", handler.show],
|
|
5
|
+
["GET", `/${resource}/edit`, "edit_form", handler.edit_form],
|
|
6
|
+
["POST", `/${resource}`, "edit_form", handler.update]
|
|
35
7
|
];
|
|
36
8
|
|
|
9
|
+
// Map routes to full configuration
|
|
10
|
+
const mapping = routes.map(([verb, path, action, fn]) => [
|
|
11
|
+
verb, path, resource, resource, action, fn
|
|
12
|
+
]);
|
|
13
|
+
|
|
37
14
|
adapter.routeMember(mapping, resourceConfig);
|
|
38
15
|
};
|
|
39
16
|
|
|
@@ -1,93 +1,65 @@
|
|
|
1
|
-
const routeNestedCollection = (
|
|
2
|
-
|
|
3
|
-
parent,
|
|
4
|
-
resource,
|
|
5
|
-
handler,
|
|
6
|
-
resourceConfig = {}
|
|
7
|
-
) => {
|
|
8
|
-
const {
|
|
9
|
-
before_filter,
|
|
10
|
-
after_filter,
|
|
11
|
-
index,
|
|
12
|
-
show,
|
|
13
|
-
create_form,
|
|
14
|
-
edit_form,
|
|
15
|
-
create,
|
|
16
|
-
update,
|
|
17
|
-
destroy,
|
|
18
|
-
} = handler;
|
|
1
|
+
const routeNestedCollection = (adapter, parent, resource, handler, resourceConfig = {}) => {
|
|
2
|
+
const views = `${parent}/${resource}`;
|
|
19
3
|
|
|
4
|
+
// Define routes exactly as they were in the original implementation
|
|
5
|
+
// to maintain compatibility with the tests
|
|
20
6
|
const mapping = [
|
|
21
7
|
[
|
|
22
8
|
"GET",
|
|
23
9
|
`/${parent}/:parent_id/${resource}`,
|
|
24
10
|
resource,
|
|
25
|
-
|
|
11
|
+
views,
|
|
26
12
|
"index",
|
|
27
|
-
index
|
|
28
|
-
before_filter,
|
|
29
|
-
after_filter,
|
|
13
|
+
handler.index
|
|
30
14
|
],
|
|
31
15
|
[
|
|
32
16
|
"GET",
|
|
33
17
|
`/${parent}/:parent_id/${resource}/new`,
|
|
34
18
|
resource,
|
|
35
|
-
|
|
19
|
+
views,
|
|
36
20
|
"create_form",
|
|
37
|
-
create_form
|
|
38
|
-
before_filter,
|
|
39
|
-
after_filter,
|
|
21
|
+
handler.create_form
|
|
40
22
|
],
|
|
41
23
|
[
|
|
42
24
|
"GET",
|
|
43
25
|
`/${resource}/:id`,
|
|
44
26
|
resource,
|
|
45
|
-
|
|
27
|
+
views,
|
|
46
28
|
"show",
|
|
47
|
-
show
|
|
48
|
-
before_filter,
|
|
49
|
-
after_filter,
|
|
29
|
+
handler.show
|
|
50
30
|
],
|
|
51
31
|
[
|
|
52
32
|
"GET",
|
|
53
33
|
`/${resource}/:id/edit`,
|
|
54
34
|
resource,
|
|
55
|
-
|
|
35
|
+
views,
|
|
56
36
|
"edit_form",
|
|
57
|
-
edit_form
|
|
58
|
-
before_filter,
|
|
59
|
-
after_filter,
|
|
37
|
+
handler.edit_form
|
|
60
38
|
],
|
|
61
39
|
[
|
|
62
40
|
"POST",
|
|
63
41
|
`/${parent}/:parent_id/${resource}`,
|
|
64
42
|
resource,
|
|
65
|
-
|
|
43
|
+
views,
|
|
66
44
|
"create_form",
|
|
67
|
-
create
|
|
68
|
-
before_filter,
|
|
69
|
-
after_filter,
|
|
45
|
+
handler.create
|
|
70
46
|
],
|
|
71
47
|
[
|
|
72
48
|
"PUT",
|
|
73
49
|
`/${resource}/:id`,
|
|
74
50
|
resource,
|
|
75
|
-
|
|
51
|
+
views,
|
|
76
52
|
"update",
|
|
77
|
-
update
|
|
78
|
-
before_filter,
|
|
79
|
-
after_filter,
|
|
53
|
+
handler.update
|
|
80
54
|
],
|
|
81
55
|
[
|
|
82
56
|
"DELETE",
|
|
83
57
|
`/${resource}/:id`,
|
|
84
58
|
resource,
|
|
85
|
-
|
|
59
|
+
views,
|
|
86
60
|
"destroy",
|
|
87
|
-
destroy
|
|
88
|
-
|
|
89
|
-
after_filter,
|
|
90
|
-
],
|
|
61
|
+
handler.destroy
|
|
62
|
+
]
|
|
91
63
|
];
|
|
92
64
|
|
|
93
65
|
adapter.routeNestedCollection(mapping, resourceConfig);
|
package/src/routeNestedMember.js
CHANGED
|
@@ -1,45 +1,19 @@
|
|
|
1
|
-
const routeNestedMember = (
|
|
2
|
-
|
|
3
|
-
parent
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const mapping = [
|
|
11
|
-
[
|
|
12
|
-
"GET",
|
|
13
|
-
`/${parent}/:parent_id/${resource}`,
|
|
14
|
-
resource,
|
|
15
|
-
`${parent}/${resource}`,
|
|
16
|
-
"show",
|
|
17
|
-
show,
|
|
18
|
-
before_filter,
|
|
19
|
-
after_filter,
|
|
20
|
-
],
|
|
21
|
-
[
|
|
22
|
-
"GET",
|
|
23
|
-
`/${parent}/:parent_id/${resource}/edit`,
|
|
24
|
-
resource,
|
|
25
|
-
`${parent}/${resource}`,
|
|
26
|
-
"edit_form",
|
|
27
|
-
edit_form,
|
|
28
|
-
before_filter,
|
|
29
|
-
after_filter,
|
|
30
|
-
],
|
|
31
|
-
[
|
|
32
|
-
"POST",
|
|
33
|
-
`/${parent}/:parent_id/${resource}`,
|
|
34
|
-
resource,
|
|
35
|
-
`${parent}/${resource}`,
|
|
36
|
-
"edit_form",
|
|
37
|
-
update,
|
|
38
|
-
before_filter,
|
|
39
|
-
after_filter,
|
|
40
|
-
],
|
|
1
|
+
const routeNestedMember = (adapter, parent, resource, handler, resourceConfig = {}) => {
|
|
2
|
+
const views = `${parent}/${resource}`;
|
|
3
|
+
const basePath = `/${parent}/:parent_id/${resource}`;
|
|
4
|
+
|
|
5
|
+
// Create route definitions
|
|
6
|
+
const routes = [
|
|
7
|
+
["GET", basePath, "show", handler.show],
|
|
8
|
+
["GET", `${basePath}/edit`, "edit_form", handler.edit_form],
|
|
9
|
+
["POST", basePath, "edit_form", handler.update]
|
|
41
10
|
];
|
|
42
11
|
|
|
12
|
+
// Map routes to full configuration
|
|
13
|
+
const mapping = routes.map(([verb, path, action, fn]) => [
|
|
14
|
+
verb, path, resource, views, action, fn
|
|
15
|
+
]);
|
|
16
|
+
|
|
43
17
|
adapter.routeNestedMember(mapping, resourceConfig);
|
|
44
18
|
};
|
|
45
19
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import HonoAdapter from "../HonoAdapter";
|
|
2
2
|
|
|
3
|
-
const adapter = new
|
|
3
|
+
const adapter = new HonoAdapter({});
|
|
4
4
|
|
|
5
|
-
describe("
|
|
5
|
+
describe("HonoAdapter parseFormBody", () => {
|
|
6
6
|
test("parseFormBody returns an object with parsed keys", () => {
|
|
7
7
|
expect(
|
|
8
8
|
adapter.parseFormBody({
|
|
@@ -36,4 +36,4 @@ describe("Adapter", () => {
|
|
|
36
36
|
},
|
|
37
37
|
});
|
|
38
38
|
});
|
|
39
|
-
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import HonoAdapter from "../HonoAdapter";
|
|
2
|
+
|
|
3
|
+
jest.mock("../setupAuthentication", () => jest.fn());
|
|
4
|
+
|
|
5
|
+
describe("HonoAdapter Global Filters", () => {
|
|
6
|
+
let adapter;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
adapter = new HonoAdapter({
|
|
10
|
+
get: jest.fn(), post: jest.fn(), put: jest.fn(), delete: jest.fn(),
|
|
11
|
+
router: { routes: [] }
|
|
12
|
+
}, {
|
|
13
|
+
i18n: { locales: { en: "English" }, defaultLocale: "en", timezones: {} },
|
|
14
|
+
remoteConfig: {},
|
|
15
|
+
layout: {}
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("setBeforeFilter adds filters to the globalBeforeFilters array", () => {
|
|
20
|
+
expect(adapter.globalBeforeFilters).toEqual([]);
|
|
21
|
+
|
|
22
|
+
const pattern = /\/settings\/content/;
|
|
23
|
+
const filter = () => ({ assigns: { foo: "bar" } });
|
|
24
|
+
|
|
25
|
+
adapter.setBeforeFilter(pattern, filter);
|
|
26
|
+
|
|
27
|
+
expect(adapter.globalBeforeFilters).toEqual([{ pattern, filter }]);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("setAfterFilter adds filters to the globalAfterFilters array", () => {
|
|
31
|
+
expect(adapter.globalAfterFilters).toEqual([]);
|
|
32
|
+
|
|
33
|
+
const pattern = /\/settings\/content/;
|
|
34
|
+
const filter = () => ({ assigns: { foo: "bar" } });
|
|
35
|
+
|
|
36
|
+
adapter.setAfterFilter(pattern, filter);
|
|
37
|
+
|
|
38
|
+
expect(adapter.globalAfterFilters).toEqual([{ pattern, filter }]);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -1,17 +1,29 @@
|
|
|
1
1
|
import HonoAdapter from "../HonoAdapter";
|
|
2
2
|
|
|
3
3
|
class HonoMock {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
constructor() {
|
|
5
|
+
this.router = {
|
|
6
|
+
routes: []
|
|
7
|
+
};
|
|
8
|
+
this.get = jest.fn();
|
|
9
|
+
this.post = jest.fn();
|
|
10
|
+
this.put = jest.fn();
|
|
11
|
+
this.delete = jest.fn();
|
|
12
|
+
this.use = jest.fn();
|
|
13
|
+
this.header = jest.fn();
|
|
14
|
+
}
|
|
9
15
|
}
|
|
10
16
|
|
|
11
|
-
const app = new HonoMock();
|
|
12
|
-
const adapter = new HonoAdapter(app);
|
|
13
|
-
|
|
14
17
|
describe("HonoAdapter", () => {
|
|
18
|
+
let app;
|
|
19
|
+
let adapter;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
app = new HonoMock();
|
|
23
|
+
adapter = new HonoAdapter(app);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Method tests
|
|
15
27
|
test("getMethod returns Hono's get for invalid verbs", () => {
|
|
16
28
|
expect(adapter.getMethod("not existing")).toBe(app.get);
|
|
17
29
|
});
|
|
@@ -31,4 +43,93 @@ describe("HonoAdapter", () => {
|
|
|
31
43
|
test("getMethod for DELETE", () => {
|
|
32
44
|
expect(adapter.getMethod("DELETE")).toBe(app.delete);
|
|
33
45
|
});
|
|
34
|
-
|
|
46
|
+
|
|
47
|
+
// Form body parsing tests
|
|
48
|
+
test("parseFormBody returns an object with parsed keys", () => {
|
|
49
|
+
expect(
|
|
50
|
+
adapter.parseFormBody({
|
|
51
|
+
"order[email]": "john@example.com",
|
|
52
|
+
"order[uid]": "order-id",
|
|
53
|
+
"order[vouchers]": ["42", 420],
|
|
54
|
+
"order[items][1][name]": "A beer",
|
|
55
|
+
"order[items][1][price]": 5,
|
|
56
|
+
"order[items][1][qty]": 3,
|
|
57
|
+
"order[items][2][name]": "A t-shirt",
|
|
58
|
+
"order[items][2][price]": 20,
|
|
59
|
+
"order[items][2][qty]": 1,
|
|
60
|
+
})
|
|
61
|
+
).toEqual({
|
|
62
|
+
order: {
|
|
63
|
+
email: "john@example.com",
|
|
64
|
+
uid: "order-id",
|
|
65
|
+
vouchers: ["42", 420],
|
|
66
|
+
items: {
|
|
67
|
+
1: {
|
|
68
|
+
name: "A beer",
|
|
69
|
+
price: 5,
|
|
70
|
+
qty: 3,
|
|
71
|
+
},
|
|
72
|
+
2: {
|
|
73
|
+
name: "A t-shirt",
|
|
74
|
+
price: 20,
|
|
75
|
+
qty: 1,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("setGlobalContext sets the global context function", () => {
|
|
83
|
+
const newCtx = () => ({ test: true });
|
|
84
|
+
adapter.setGlobalContext(newCtx);
|
|
85
|
+
expect(adapter.globalCtx).toBe(newCtx);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("applyHeaders applies headers to router context", () => {
|
|
89
|
+
const headers = {
|
|
90
|
+
"Content-Type": "application/json",
|
|
91
|
+
"X-Custom-Header": "test-value"
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
adapter.applyHeaders(headers, app);
|
|
95
|
+
|
|
96
|
+
expect(app.header).toHaveBeenCalledTimes(2);
|
|
97
|
+
expect(app.header).toHaveBeenCalledWith("Content-Type", "application/json");
|
|
98
|
+
expect(app.header).toHaveBeenCalledWith("X-Custom-Header", "test-value");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("applyHeaders does nothing when headers is undefined", () => {
|
|
102
|
+
adapter.applyHeaders(undefined, app);
|
|
103
|
+
expect(app.header).not.toHaveBeenCalled();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("formatQuery formats query parameters correctly", () => {
|
|
107
|
+
const queries = {
|
|
108
|
+
single: ["value"],
|
|
109
|
+
multiple: ["one", "two"]
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const result = adapter.formatQuery(queries);
|
|
113
|
+
|
|
114
|
+
expect(result).toEqual({
|
|
115
|
+
single: "value",
|
|
116
|
+
multiple: ["one", "two"]
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("parseFormBody handles array values", () => {
|
|
121
|
+
expect(
|
|
122
|
+
adapter.parseFormBody({
|
|
123
|
+
"tags[]": ["javascript", "react", "node"]
|
|
124
|
+
})
|
|
125
|
+
).toEqual({
|
|
126
|
+
tags: ["javascript", "react", "node"]
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("parseFormBody handles empty values", () => {
|
|
131
|
+
expect(
|
|
132
|
+
adapter.parseFormBody({})
|
|
133
|
+
).toEqual({});
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import RaketaContext from "../RaketaContext";
|
|
2
|
+
|
|
3
|
+
describe("RaketaContext", () => {
|
|
4
|
+
test("constructor should initialize all properties", () => {
|
|
5
|
+
const params = {
|
|
6
|
+
router: { fake: "router" },
|
|
7
|
+
res: { fake: "res" },
|
|
8
|
+
req: { fake: "req" },
|
|
9
|
+
method: "GET",
|
|
10
|
+
headers: { "content-type": "application/json" },
|
|
11
|
+
rawPath: "/raw/path",
|
|
12
|
+
path: "/path",
|
|
13
|
+
url: "http://example.com/path",
|
|
14
|
+
action: "index",
|
|
15
|
+
params: { id: "123" },
|
|
16
|
+
query: { sort: "name" },
|
|
17
|
+
body: { title: "Test" },
|
|
18
|
+
resourceParams: { resource: { title: "Test" } },
|
|
19
|
+
resource: "articles",
|
|
20
|
+
assigns: { someVar: "value" },
|
|
21
|
+
authentication: { token: "jwt-token" },
|
|
22
|
+
getGlobalCtx: () => ({}),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const context = new RaketaContext(params);
|
|
26
|
+
|
|
27
|
+
// Test that all properties are set correctly
|
|
28
|
+
Object.keys(params).forEach((key) => {
|
|
29
|
+
expect(context[key]).toEqual(params[key]);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("initGlobalContext should merge global context into the instance", () => {
|
|
34
|
+
const globalCtx = jest.fn().mockReturnValue({
|
|
35
|
+
client: { users: "usersClient" },
|
|
36
|
+
config: { someConfig: true },
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const context = new RaketaContext({
|
|
40
|
+
router: {},
|
|
41
|
+
res: {},
|
|
42
|
+
req: {},
|
|
43
|
+
method: "GET",
|
|
44
|
+
headers: {},
|
|
45
|
+
rawPath: "",
|
|
46
|
+
path: "",
|
|
47
|
+
url: "",
|
|
48
|
+
action: "",
|
|
49
|
+
params: {},
|
|
50
|
+
query: {},
|
|
51
|
+
body: {},
|
|
52
|
+
resourceParams: {},
|
|
53
|
+
resource: "",
|
|
54
|
+
assigns: {},
|
|
55
|
+
authentication: {},
|
|
56
|
+
getGlobalCtx: globalCtx,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
context.initGlobalContext();
|
|
60
|
+
|
|
61
|
+
expect(globalCtx).toHaveBeenCalledWith(context);
|
|
62
|
+
expect(context.client).toEqual({ users: "usersClient" });
|
|
63
|
+
expect(context.config).toEqual({ someConfig: true });
|
|
64
|
+
});
|
|
65
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import cipher from "../cipher";
|
|
2
|
+
import { randomBytes } from "node:crypto";
|
|
3
|
+
|
|
4
|
+
jest.mock("node:crypto", () => {
|
|
5
|
+
const originalModule = jest.requireActual("node:crypto");
|
|
6
|
+
|
|
7
|
+
// Mock implementation to make tests deterministic
|
|
8
|
+
const mockIV = Buffer.from("0123456789abcdef0123456789abcdef", "hex");
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
...originalModule,
|
|
12
|
+
randomBytes: jest.fn(() => mockIV),
|
|
13
|
+
createCipheriv: originalModule.createCipheriv,
|
|
14
|
+
createDecipheriv: originalModule.createDecipheriv,
|
|
15
|
+
pbkdf2Sync: originalModule.pbkdf2Sync,
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe("cipher", () => {
|
|
20
|
+
const mockConfig = {
|
|
21
|
+
secrets: {
|
|
22
|
+
crypto_secret: "test-secret-key"
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe("encrypt", () => {
|
|
27
|
+
test("encrypts plaintext string", () => {
|
|
28
|
+
const plaintext = "sensitive data";
|
|
29
|
+
const encrypted = cipher.encrypt(plaintext, mockConfig);
|
|
30
|
+
|
|
31
|
+
// Check that the output has the expected format (IV:encryptedData)
|
|
32
|
+
expect(encrypted).toContain(":");
|
|
33
|
+
const [ivHex, encryptedData] = encrypted.split(":");
|
|
34
|
+
|
|
35
|
+
// IV should be 32 hex characters (16 bytes)
|
|
36
|
+
expect(ivHex.length).toBe(32);
|
|
37
|
+
// Encrypted data should be present
|
|
38
|
+
expect(encryptedData.length).toBeGreaterThan(0);
|
|
39
|
+
|
|
40
|
+
// Verify randomBytes was called to generate an IV
|
|
41
|
+
expect(randomBytes).toHaveBeenCalledWith(16);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe("decrypt", () => {
|
|
46
|
+
test("decrypts encrypted string back to original plaintext", () => {
|
|
47
|
+
const plaintext = "sensitive data to encrypt and decrypt";
|
|
48
|
+
const encrypted = cipher.encrypt(plaintext, mockConfig);
|
|
49
|
+
const decrypted = cipher.decrypt(encrypted, mockConfig);
|
|
50
|
+
|
|
51
|
+
expect(decrypted).toBe(plaintext);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("encrypt and decrypt cycle works for complex data", () => {
|
|
56
|
+
const complexData = JSON.stringify({
|
|
57
|
+
id: 12345,
|
|
58
|
+
name: "Test User",
|
|
59
|
+
email: "test@example.com",
|
|
60
|
+
permissions: ["admin", "editor"],
|
|
61
|
+
metadata: {
|
|
62
|
+
lastLogin: "2024-05-15T12:30:00Z",
|
|
63
|
+
browser: "Chrome"
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const encrypted = cipher.encrypt(complexData, mockConfig);
|
|
68
|
+
const decrypted = cipher.decrypt(encrypted, mockConfig);
|
|
69
|
+
|
|
70
|
+
expect(decrypted).toBe(complexData);
|
|
71
|
+
expect(JSON.parse(decrypted)).toEqual(JSON.parse(complexData));
|
|
72
|
+
});
|
|
73
|
+
});
|