@openally/github.sdk 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.
@@ -0,0 +1,221 @@
1
+ // Import Node.js Dependencies
2
+ import { describe, it, beforeEach, afterEach } from "node:test";
3
+ import assert from "node:assert/strict";
4
+
5
+ // Import Third-party Dependencies
6
+ import { MockAgent, setGlobalDispatcher, getGlobalDispatcher, type Dispatcher } from "undici";
7
+
8
+ // Import Internal Dependencies
9
+ import { ApiEndpoint } from "../src/class/ApiEndpoint.ts";
10
+ import { repos, createReposProxy } from "../src/api/repos.ts";
11
+
12
+ // CONSTANTS
13
+ const kGithubOrigin = "https://api.github.com";
14
+
15
+ describe("Repos API", () => {
16
+ let mockAgent: MockAgent;
17
+ let originalDispatcher: Dispatcher;
18
+
19
+ beforeEach(() => {
20
+ originalDispatcher = getGlobalDispatcher();
21
+ mockAgent = new MockAgent();
22
+ mockAgent.disableNetConnect();
23
+ setGlobalDispatcher(mockAgent);
24
+ });
25
+
26
+ afterEach(async() => {
27
+ await mockAgent.close();
28
+ setGlobalDispatcher(originalDispatcher);
29
+ });
30
+
31
+ describe("createReposProxy()", () => {
32
+ it("should return an ApiEndpoint for each simple repo endpoint", () => {
33
+ const proxy = createReposProxy();
34
+ const methods = proxy.owner.myrepo;
35
+
36
+ assert.ok(methods.tags() instanceof ApiEndpoint);
37
+ assert.ok(methods.pulls() instanceof ApiEndpoint);
38
+ assert.ok(methods.issues() instanceof ApiEndpoint);
39
+ assert.ok(methods.commits() instanceof ApiEndpoint);
40
+ assert.ok(methods.workflows() instanceof ApiEndpoint);
41
+ });
42
+
43
+ it("should return an ApiEndpoint from workflowRuns() with a string workflow id", () => {
44
+ const endpoint = createReposProxy().owner.myrepo.workflowRuns("ci.yml");
45
+
46
+ assert.ok(endpoint instanceof ApiEndpoint);
47
+ });
48
+
49
+ it("should return an ApiEndpoint from workflowRuns() with a numeric workflow id", () => {
50
+ const endpoint = createReposProxy().owner.myrepo.workflowRuns(42);
51
+
52
+ assert.ok(endpoint instanceof ApiEndpoint);
53
+ });
54
+
55
+ it("should return an ApiEndpoint from runJobs()", () => {
56
+ const endpoint = createReposProxy().owner.myrepo.runJobs(123);
57
+
58
+ assert.ok(endpoint instanceof ApiEndpoint);
59
+ });
60
+
61
+ it("should return an ApiEndpoint from runArtifacts()", () => {
62
+ const endpoint = createReposProxy().owner.myrepo.runArtifacts(456);
63
+
64
+ assert.ok(endpoint instanceof ApiEndpoint);
65
+ });
66
+
67
+ it("should create a new ApiEndpoint on each method call", () => {
68
+ const proxy = createReposProxy();
69
+
70
+ assert.notStrictEqual(proxy.owner.myrepo.tags(), proxy.owner.myrepo.tags());
71
+ });
72
+
73
+ it("should pass the token from config to ApiEndpoints", async() => {
74
+ const proxy = createReposProxy({ token: "repotoken" });
75
+
76
+ mockAgent
77
+ .get(kGithubOrigin)
78
+ .intercept({
79
+ path: "/repos/owner/myrepo/tags",
80
+ method: "GET",
81
+ headers: { authorization: "token repotoken" }
82
+ })
83
+ .reply(200, JSON.stringify([]), {
84
+ headers: { "content-type": "application/json" }
85
+ });
86
+
87
+ await assert.doesNotReject(proxy.owner.myrepo.tags().all());
88
+ });
89
+ });
90
+
91
+ describe("repos (default export)", () => {
92
+ it("should fetch tags for a repo", async() => {
93
+ mockAgent
94
+ .get(kGithubOrigin)
95
+ .intercept({ path: "/repos/octocat/hello-world/tags", method: "GET" })
96
+ .reply(200, JSON.stringify([{ name: "v1.0.0" }, { name: "v1.1.0" }]), {
97
+ headers: { "content-type": "application/json" }
98
+ });
99
+
100
+ const result = await repos.octocat["hello-world"].tags().all();
101
+
102
+ assert.deepEqual(result, [{ name: "v1.0.0" }, { name: "v1.1.0" }]);
103
+ });
104
+
105
+ it("should fetch pull requests for a repo", async() => {
106
+ mockAgent
107
+ .get(kGithubOrigin)
108
+ .intercept({ path: "/repos/octocat/hello-world/pulls", method: "GET" })
109
+ .reply(200, JSON.stringify([{ number: 1, title: "Fix bug" }]), {
110
+ headers: { "content-type": "application/json" }
111
+ });
112
+
113
+ const result = await repos.octocat["hello-world"].pulls().all();
114
+
115
+ assert.deepEqual(result, [{ number: 1, title: "Fix bug" }]);
116
+ });
117
+
118
+ it("should fetch issues for a repo", async() => {
119
+ mockAgent
120
+ .get(kGithubOrigin)
121
+ .intercept({ path: "/repos/octocat/hello-world/issues", method: "GET" })
122
+ .reply(200, JSON.stringify([{ number: 5, title: "Bug report" }]), {
123
+ headers: { "content-type": "application/json" }
124
+ });
125
+
126
+ const result = await repos.octocat["hello-world"].issues().all();
127
+
128
+ assert.deepEqual(result, [{ number: 5, title: "Bug report" }]);
129
+ });
130
+
131
+ it("should fetch commits for a repo", async() => {
132
+ mockAgent
133
+ .get(kGithubOrigin)
134
+ .intercept({ path: "/repos/octocat/hello-world/commits", method: "GET" })
135
+ .reply(200, JSON.stringify([{ sha: "abc123" }]), {
136
+ headers: { "content-type": "application/json" }
137
+ });
138
+
139
+ const result = await repos.octocat["hello-world"].commits().all();
140
+
141
+ assert.deepEqual(result, [{ sha: "abc123" }]);
142
+ });
143
+
144
+ it("should fetch workflows using the extractor", async() => {
145
+ mockAgent
146
+ .get(kGithubOrigin)
147
+ .intercept({ path: "/repos/octocat/hello-world/actions/workflows", method: "GET" })
148
+ .reply(200, JSON.stringify({ total_count: 1, workflows: [{ id: 1, name: "CI" }] }), {
149
+ headers: { "content-type": "application/json" }
150
+ });
151
+
152
+ const result = await repos.octocat["hello-world"].workflows().all();
153
+
154
+ assert.deepEqual(result, [{ id: 1, name: "CI" }]);
155
+ });
156
+
157
+ it("should fetch workflow runs by string workflow id using the extractor", async() => {
158
+ mockAgent
159
+ .get(kGithubOrigin)
160
+ .intercept({
161
+ path: "/repos/octocat/hello-world/actions/workflows/ci.yml/runs",
162
+ method: "GET"
163
+ })
164
+ .reply(200, JSON.stringify({ total_count: 1, workflow_runs: [{ id: 100, status: "completed" }] }), {
165
+ headers: { "content-type": "application/json" }
166
+ });
167
+
168
+ const result = await repos.octocat["hello-world"].workflowRuns("ci.yml").all();
169
+
170
+ assert.deepEqual(result, [{ id: 100, status: "completed" }]);
171
+ });
172
+
173
+ it("should fetch workflow runs by numeric workflow id using the extractor", async() => {
174
+ mockAgent
175
+ .get(kGithubOrigin)
176
+ .intercept({
177
+ path: "/repos/octocat/hello-world/actions/workflows/42/runs",
178
+ method: "GET"
179
+ })
180
+ .reply(200, JSON.stringify({ total_count: 2, workflow_runs: [{ id: 200 }, { id: 201 }] }), {
181
+ headers: { "content-type": "application/json" }
182
+ });
183
+
184
+ const result = await repos.octocat["hello-world"].workflowRuns(42).all();
185
+
186
+ assert.deepEqual(result, [{ id: 200 }, { id: 201 }]);
187
+ });
188
+
189
+ it("should fetch run jobs using the extractor", async() => {
190
+ mockAgent
191
+ .get(kGithubOrigin)
192
+ .intercept({
193
+ path: "/repos/octocat/hello-world/actions/runs/999/jobs",
194
+ method: "GET"
195
+ })
196
+ .reply(200, JSON.stringify({ total_count: 1, jobs: [{ id: 10, name: "build" }] }), {
197
+ headers: { "content-type": "application/json" }
198
+ });
199
+
200
+ const result = await repos.octocat["hello-world"].runJobs(999).all();
201
+
202
+ assert.deepEqual(result, [{ id: 10, name: "build" }]);
203
+ });
204
+
205
+ it("should fetch run artifacts using the extractor", async() => {
206
+ mockAgent
207
+ .get(kGithubOrigin)
208
+ .intercept({
209
+ path: "/repos/octocat/hello-world/actions/runs/999/artifacts",
210
+ method: "GET"
211
+ })
212
+ .reply(200, JSON.stringify({ total_count: 1, artifacts: [{ id: 55, name: "build-output" }] }), {
213
+ headers: { "content-type": "application/json" }
214
+ });
215
+
216
+ const result = await repos.octocat["hello-world"].runArtifacts(999).all();
217
+
218
+ assert.deepEqual(result, [{ id: 55, name: "build-output" }]);
219
+ });
220
+ });
221
+ });
@@ -0,0 +1,159 @@
1
+ // Import Node.js Dependencies
2
+ import { describe, it, beforeEach, afterEach } from "node:test";
3
+ import assert from "node:assert/strict";
4
+
5
+ // Import Third-party Dependencies
6
+ import { MockAgent, setGlobalDispatcher, getGlobalDispatcher, type Dispatcher } from "undici";
7
+
8
+ // Import Internal Dependencies
9
+ import { ApiEndpoint } from "../src/class/ApiEndpoint.ts";
10
+ import { users, createUsersProxy } from "../src/api/users.ts";
11
+
12
+ // CONSTANTS
13
+ const kGithubOrigin = "https://api.github.com";
14
+ const kUserEndpoints = ["orgs", "repos", "gists", "followers", "following", "starred"] as const;
15
+
16
+ describe("Users API", () => {
17
+ let mockAgent: MockAgent;
18
+ let originalDispatcher: Dispatcher;
19
+
20
+ beforeEach(() => {
21
+ originalDispatcher = getGlobalDispatcher();
22
+ mockAgent = new MockAgent();
23
+ mockAgent.disableNetConnect();
24
+ setGlobalDispatcher(mockAgent);
25
+ });
26
+
27
+ afterEach(async() => {
28
+ await mockAgent.close();
29
+ setGlobalDispatcher(originalDispatcher);
30
+ });
31
+
32
+ describe("createUsersProxy()", () => {
33
+ it("should return an ApiEndpoint for each standard user endpoint", () => {
34
+ const proxy = createUsersProxy();
35
+
36
+ for (const endpoint of kUserEndpoints) {
37
+ const result = proxy.testuser[endpoint]();
38
+ assert.ok(result instanceof ApiEndpoint, `${endpoint}() should return an ApiEndpoint`);
39
+ }
40
+ });
41
+
42
+ it("should create independent ApiEndpoints per username", () => {
43
+ const proxy = createUsersProxy();
44
+
45
+ const fooOrgs = proxy.foo.orgs();
46
+ const barOrgs = proxy.bar.orgs();
47
+
48
+ assert.ok(fooOrgs instanceof ApiEndpoint);
49
+ assert.ok(barOrgs instanceof ApiEndpoint);
50
+ assert.notStrictEqual(fooOrgs, barOrgs);
51
+ });
52
+
53
+ it("should pass the token from config to the ApiEndpoint", async() => {
54
+ const proxy = createUsersProxy({ token: "mytoken" });
55
+
56
+ mockAgent
57
+ .get(kGithubOrigin)
58
+ .intercept({
59
+ path: "/users/testuser/repos",
60
+ method: "GET",
61
+ headers: { authorization: "token mytoken" }
62
+ })
63
+ .reply(200, JSON.stringify([]), {
64
+ headers: { "content-type": "application/json" }
65
+ });
66
+
67
+ await assert.doesNotReject(proxy.testuser.repos().all());
68
+ });
69
+
70
+ it("should create a new ApiEndpoint on each method call", () => {
71
+ const proxy = createUsersProxy();
72
+
73
+ const first = proxy.testuser.repos();
74
+ const second = proxy.testuser.repos();
75
+
76
+ assert.notStrictEqual(first, second);
77
+ });
78
+ });
79
+
80
+ describe("users (default export)", () => {
81
+ it("should be a UsersProxy with no token", async() => {
82
+ mockAgent
83
+ .get(kGithubOrigin)
84
+ .intercept({ path: "/users/octocat/orgs", method: "GET" })
85
+ .reply(200, JSON.stringify([{ id: 1, login: "github" }]), {
86
+ headers: { "content-type": "application/json" }
87
+ });
88
+
89
+ const result = await users.octocat.orgs().all();
90
+
91
+ assert.deepEqual(result, [{ id: 1, login: "github" }]);
92
+ });
93
+
94
+ it("should fetch repos for a user", async() => {
95
+ mockAgent
96
+ .get(kGithubOrigin)
97
+ .intercept({ path: "/users/octocat/repos", method: "GET" })
98
+ .reply(200, JSON.stringify([{ id: 42, name: "hello-world" }]), {
99
+ headers: { "content-type": "application/json" }
100
+ });
101
+
102
+ const result = await users.octocat.repos().all();
103
+
104
+ assert.deepEqual(result, [{ id: 42, name: "hello-world" }]);
105
+ });
106
+
107
+ it("should fetch followers for a user", async() => {
108
+ mockAgent
109
+ .get(kGithubOrigin)
110
+ .intercept({ path: "/users/octocat/followers", method: "GET" })
111
+ .reply(200, JSON.stringify([{ login: "user1" }, { login: "user2" }]), {
112
+ headers: { "content-type": "application/json" }
113
+ });
114
+
115
+ const result = await users.octocat.followers().all();
116
+
117
+ assert.deepEqual(result, [{ login: "user1" }, { login: "user2" }]);
118
+ });
119
+
120
+ it("should fetch following for a user", async() => {
121
+ mockAgent
122
+ .get(kGithubOrigin)
123
+ .intercept({ path: "/users/octocat/following", method: "GET" })
124
+ .reply(200, JSON.stringify([{ login: "user3" }]), {
125
+ headers: { "content-type": "application/json" }
126
+ });
127
+
128
+ const result = await users.octocat.following().all();
129
+
130
+ assert.deepEqual(result, [{ login: "user3" }]);
131
+ });
132
+
133
+ it("should fetch gists for a user", async() => {
134
+ mockAgent
135
+ .get(kGithubOrigin)
136
+ .intercept({ path: "/users/octocat/gists", method: "GET" })
137
+ .reply(200, JSON.stringify([{ id: "abc123" }]), {
138
+ headers: { "content-type": "application/json" }
139
+ });
140
+
141
+ const result = await users.octocat.gists().all();
142
+
143
+ assert.deepEqual(result, [{ id: "abc123" }]);
144
+ });
145
+
146
+ it("should fetch starred repos for a user", async() => {
147
+ mockAgent
148
+ .get(kGithubOrigin)
149
+ .intercept({ path: "/users/octocat/starred", method: "GET" })
150
+ .reply(200, JSON.stringify([{ id: 99, name: "starred-repo" }]), {
151
+ headers: { "content-type": "application/json" }
152
+ });
153
+
154
+ const result = await users.octocat.starred().all();
155
+
156
+ assert.deepEqual(result, [{ id: 99, name: "starred-repo" }]);
157
+ });
158
+ });
159
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "@openally/config.typescript/esm-ts-next",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "./src",
6
+ "types": ["node"],
7
+ "lib": ["ES2022", "ES2023", "ES2024", "ESNext"],
8
+ },
9
+ "include": ["src"],
10
+ "exclude": ["node_modules", "dist"]
11
+ }