aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2021-07-29 15:54:04 +0200
committerHampusM <hampus@hampusmat.com>2021-07-29 15:59:52 +0200
commit9d6d2ce8cf34af6e787dae901ee3e5914c4c0fa4 (patch)
tree97f522340e954fe4ae6a0da2f8801351740cb990
parent8d42b6f44ca5b0eeb54938e1cf9509d1f8294f00 (diff)
Added tests & API tests uses Axios
Added a unit test for creating a archive & added a int test for downloading a tag tarball
-rw-r--r--package.json1
-rw-r--r--test/int/api.int.test.ts237
-rw-r--r--test/unit/tree.unit.test.ts60
-rw-r--r--yarn.lock9
4 files changed, 201 insertions, 106 deletions
diff --git a/package.json b/package.json
index 239b00b..0927da7 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"devDependencies": {
"@types/fs-extra": "^9.0.12",
"@types/jest": "^26.0.23",
+ "axios": "^0.21.1",
"concurrently": "^6.2.0",
"dotenv": "^10.0.0",
"eslint": "^7.31.0",
diff --git a/test/int/api.int.test.ts b/test/int/api.int.test.ts
index 2f7ead2..b93a947 100644
--- a/test/int/api.int.test.ts
+++ b/test/int/api.int.test.ts
@@ -2,9 +2,10 @@ import { FastifyInstance } from "fastify";
import buildApp from "server/src/app";
import { EnvironmentVariables } from "../util";
import { readFile } from "fs-extra";
-import { exec, ExecException } from "child_process";
-import { Response } from "light-my-request";
import { Info, Repository, RepositorySummary } from "../../packages/shared_types/src";
+import axios, { AxiosResponse } from "axios";
+import { Unzip } from "zlib";
+import { Readable } from "stream";
const env = process.env as EnvironmentVariables;
@@ -14,7 +15,7 @@ const port = 8080;
describe("API", () => {
let app: FastifyInstance;
- beforeAll(() => {
+ beforeAll(async() => {
app = buildApp({
host: host,
port: port,
@@ -24,6 +25,10 @@ describe("API", () => {
base_dir: env.BASE_DIR,
production: false
}, "");
+
+ await app.listen(port);
+
+ axios.defaults.baseURL = `http://${host}:${port}`;
});
afterAll(async() => {
@@ -31,30 +36,27 @@ describe("API", () => {
});
describe("GET /api/v1/info", () => {
- let res: Response;
+ let res: AxiosResponse;
beforeAll(async() => {
- res = await app.inject({
- method: "GET",
- url: "api/v1/info"
- });
+ res = await axios.get("/api/v1/info");
});
it("Should respond", () => {
expect.assertions(4);
expect(res).toBeDefined();
- expect(res).toHaveProperty("statusCode");
- expect(res).toHaveProperty("payload");
- expect(res.payload).toBeDefined();
+ expect(res).toHaveProperty("status");
+ expect(res).toHaveProperty("data");
+ expect(res.data).toBeDefined();
});
it("Should respond without an error", () => {
expect.assertions(5);
- expect(res.statusCode).toEqual(200);
+ expect(res.status).toEqual(200);
- const json: Record<string, string> = res.json();
+ const json: Record<string, string> = res.data;
expect(json).toBeDefined();
expect(json.error).toBeUndefined();
@@ -65,7 +67,7 @@ describe("API", () => {
it("Should have a valid response", () => {
expect.assertions(4);
- const info = res.json().data as Info;
+ const info = res.data.data as Info;
expect(info).toHaveProperty("title");
expect(info.title).toEqual("Bob's cool projects");
@@ -76,30 +78,27 @@ describe("API", () => {
});
describe("GET /api/v1/repos", () => {
- let res: Response;
+ let res: AxiosResponse;
beforeAll(async() => {
- res = await app.inject({
- method: "GET",
- url: "api/v1/repos"
- });
+ res = await axios.get("/api/v1/repos");
});
it("Should respond", () => {
expect.assertions(4);
expect(res).toBeDefined();
- expect(res).toHaveProperty("statusCode");
- expect(res).toHaveProperty("payload");
- expect(res.payload).toBeDefined();
+ expect(res).toHaveProperty("status");
+ expect(res).toHaveProperty("data");
+ expect(res.data).toBeDefined();
});
it("Should respond without an error", () => {
expect.assertions(5);
- expect(res.statusCode).toEqual(200);
+ expect(res.status).toEqual(200);
- const json: Record<string, string> = res.json();
+ const json: Record<string, string> = res.data;
expect(json).toBeDefined();
expect(json.error).toBeUndefined();
@@ -110,7 +109,7 @@ describe("API", () => {
it("Should have a valid response", () => {
expect.hasAssertions();
- const repositories = res.json().data as RepositorySummary[];
+ const repositories = res.data.data as RepositorySummary[];
expect(repositories).toBeDefined();
@@ -129,30 +128,27 @@ describe("API", () => {
});
describe("GET /api/v1/repos/:repository", () => {
- let res: Response;
+ let res: AxiosResponse;
beforeAll(async() => {
- res = await app.inject({
- method: "GET",
- url: `api/v1/repos/${env.AVAIL_REPO.slice(0, -4)}`
- });
+ res = await axios.get(`/api/v1/repos/${env.AVAIL_REPO.slice(0, -4)}`);
});
it("Should respond", () => {
expect.assertions(4);
expect(res).toBeDefined();
- expect(res).toHaveProperty("statusCode");
- expect(res).toHaveProperty("payload");
- expect(res.payload).toBeDefined();
+ expect(res).toHaveProperty("status");
+ expect(res).toHaveProperty("data");
+ expect(res.data).toBeDefined();
});
it("Should respond without an error", () => {
expect.assertions(5);
- expect(res.statusCode).toEqual(200);
+ expect(res.status).toEqual(200);
- const json: Record<string, string> = res.json();
+ const json: Record<string, string> = res.data;
expect(json).toBeDefined();
expect(json.error).toBeUndefined();
@@ -163,7 +159,7 @@ describe("API", () => {
it("Should have a valid response", () => {
expect.hasAssertions();
- const repository = res.json().data as Repository;
+ const repository = res.data.data as Repository;
expect(repository).toHaveProperty("name");
expect(repository.name).toEqual(env.AVAIL_REPO.slice(0, -4));
@@ -176,32 +172,75 @@ describe("API", () => {
});
});
+ describe("GET /:repository/refs/tags/:tag", () => {
+ let res: AxiosResponse;
+
+ beforeAll(async() => {
+ res = await axios.get(`/${env.AVAIL_REPO.slice(0, -4)}/refs/tags/1.2`, {
+ responseType: "stream"
+ });
+ });
+
+ it("Should respond", () => {
+ expect.assertions(4);
+
+ expect(res).toBeDefined();
+ expect(res).toHaveProperty("status");
+ expect(res).toHaveProperty("data");
+ expect(res.data).toBeDefined();
+ });
+
+ it("Should respond without an error", () => {
+ expect.assertions(1);
+
+ expect(res.status).toEqual(200);
+ });
+
+ it("Should have a valid response", async() => {
+ expect(res).toBeDefined();
+
+ const data = res.data as Unzip;
+
+ const content: Buffer[] = [];
+
+ data.on("data", (chunk: Buffer) => {
+ content.push(chunk);
+ });
+
+ await new Promise(resolve => {
+ data.on("end", () => {
+ resolve(null);
+ });
+ });
+
+ expect(content.length).toBeGreaterThanOrEqual(1);
+ expect(content.join().toString()).toBeDefined();
+ });
+ });
+
describe("Git HTTP", () => {
describe("GET /:repository/info/refs", () => {
- let res: Response;
+ let res: AxiosResponse;
beforeAll(async() => {
- res = await app.inject({
- method: "GET",
- url: `${env.AVAIL_REPO}/info/refs?service=git-upload-pack`
- });
+ res = await axios.get(`/${env.AVAIL_REPO}/info/refs?service=git-upload-pack`);
});
it("Should respond", () => {
expect.assertions(4);
expect(res).toBeDefined();
- expect(res).toHaveProperty("statusCode");
- expect(res).toHaveProperty("payload");
- expect(res.payload).toBeDefined();
+ expect(res).toHaveProperty("status");
+ expect(res).toHaveProperty("data");
+ expect(res.data).toBeDefined();
});
it("Should have a valid response when the service is git-upload-pack", () => {
expect.assertions(3);
- expect(res.statusCode).toEqual(200);
+ expect(res.status).toEqual(200);
- const payload_lines = res.payload.split("\n");
+ const payload_lines = res.data.split("\n");
expect(payload_lines[0]).toEqual("001e# service=git-upload-pack");
expect(payload_lines[payload_lines.length - 1]).toEqual("0000");
@@ -210,89 +249,82 @@ describe("API", () => {
it("Should respond with 403 when the service is git-receive-pack", async() => {
expect.assertions(3);
- const err_res = await app.inject({
- method: "GET",
- url: `${env.AVAIL_REPO}/info/refs?service=git-receive-pack`
+ const err_res = await axios.get(`/${env.AVAIL_REPO}/info/refs?service=git-receive-pack`, {
+ validateStatus: () => true
});
expect(err_res).toBeDefined();
- expect(err_res).toHaveProperty("statusCode");
- expect(err_res.statusCode).toEqual(403);
+ expect(err_res).toHaveProperty("status");
+ expect(err_res.status).toEqual(403);
});
});
describe("POST /:repository/git-upload-pack", () => {
- let stdout: Buffer;
- let stderr: Buffer;
+ let res: AxiosResponse;
beforeAll(async() => {
- await app.listen(port);
+ const body = new Readable({ read: () => null });
const head = /^[a-f0-9]+/.exec((await readFile(`${env.BASE_DIR}/${env.AVAIL_REPO}/FETCH_HEAD`)).toString())[0];
- const data = `0098want ${head} multi_ack_detailed no-done side-band-64k thin-pack ofs-delta deepen-since deepen-not agent=git/2.32.0\n00000009done`;
-
- /* Send a post request to git-upload-pack with curl
-
- I did it this way because i just couldn't get chunked responses
- to work with LightMyRequest or Supertest */
- const res = new Promise((resolve: (value: Record<string, Buffer>) => void, reject: (value: ExecException) => void) => {
- const curl_params = [
- "-X POST",
- "-sS",
- "-f",
- "-H \"Content-Type: application/x-git-upload-pack-request\"",
- "-T -"
- ].join(" ");
- const command = `echo "${data}" | curl ${curl_params} http://${host}:${port}/${env.AVAIL_REPO}/git-upload-pack`;
-
- exec(command, { maxBuffer: 5368709120, encoding: "buffer" }, (err, stdout, stderr) => {
- if(err) {
- reject(err);
- return;
- }
- resolve({ stdout, stderr });
- });
- });
- ({ stdout, stderr } = await res);
+ body.push(`0098want ${head} multi_ack_detailed no-done side-band-64k thin-pack ofs-delta deepen-since deepen-not agent=git/2.32.0\n00000009done\n`);
+ body.push(null);
+
+ res = await axios.post(`/${env.AVAIL_REPO.slice(0, -4)}/git-upload-pack`, body, {
+ responseType: "arraybuffer",
+ headers: {
+ "Content-Type": "application/x-git-upload-pack-request",
+ Accept: "*/*",
+ "Transfer-Encoding": "chunked",
+ Connection: "keep-alive",
+ Expect: "100-continue"
+ },
+ data: body,
+ decompress: false
+ });
});
it("Should respond", () => {
- expect.assertions(2);
+ expect.assertions(4);
- expect(stdout).toBeInstanceOf(Buffer);
- expect(stdout.length).toBeGreaterThan(5);
+ expect(res).toBeDefined();
+ expect(res).toHaveProperty("status");
+ expect(res).toHaveProperty("data");
+ expect(res.data).toBeDefined();
});
it("Should respond without an error", () => {
- expect.assertions(2);
+ expect.assertions(1);
- expect(stderr).toBeInstanceOf(Buffer);
- expect(stderr.length).toEqual(0);
+ expect(res.status).toEqual(200);
});
- it("Should have a valid response", () => {
+ it("Should have a valid response", async() => {
expect.hasAssertions();
- const readable_stdout = stdout.toString().split("\n");
+ const data_lines = res.data.toString().split("\n");
- expect(readable_stdout.length).toBeGreaterThan(5);
+ expect(data_lines.length).toBeGreaterThan(5);
- readable_stdout[2] = readable_stdout[2]
+ /*
+ Replaces carriage returns with '###' and removes some headers & some unnecessary stuff at the last line.
+ This is so that parsing is easier.
+ */
+ data_lines[2] = data_lines[2]
.replace(/[\x0D]/g, "###")
.replace(/[0-9a-f]{4}[\x02]/g, "");
- readable_stdout[3] = readable_stdout[3]
+ data_lines[3] = data_lines[3]
.replace(/[\x0D]/g, "###")
.replace(/[0-9a-f]{4}[\x02]/g, "");
- readable_stdout[readable_stdout.length - 1] = readable_stdout[readable_stdout.length - 1].replace(/.*[\x01]/, "");
+ data_lines[data_lines.length - 1] = data_lines[data_lines.length - 1].replace(/.*[\x01]/, "");
- expect(readable_stdout[0]).toEqual("0008NAK");
- expect(readable_stdout[1]).toMatch(/Enumerating objects: \d+, done\.$/);
+ expect(data_lines[0]).toEqual("0008NAK");
+ expect(data_lines[1]).toMatch(/Enumerating objects: \d+, done\.$/);
// Make sure the progress output for counting objects is fine and dandy
- const counting_objects = readable_stdout[2].split("###");
+ const counting_objects = data_lines[2].split("###");
for(const progress of counting_objects.slice(0, -1)) {
expect(progress).toMatch(/^Counting objects:\s+\d+%\s\(\d+\/\d+\)/);
@@ -301,7 +333,7 @@ describe("API", () => {
expect(counting_objects[counting_objects.length - 1]).toMatch(/^Counting objects:\s+\d+%\s\(\d+\/\d+\),\sdone\./);
// Make sure the progress output for compressing objects is fine and dandy
- const compressing_objects = readable_stdout[3].split("###");
+ const compressing_objects = data_lines[3].split("###");
for(const progress of compressing_objects.slice(0, -1)) {
expect(progress).toMatch(/^Compressing objects:\s+\d+%\s\(\d+\/\d+\)/);
@@ -310,7 +342,7 @@ describe("API", () => {
expect(compressing_objects[counting_objects.length - 1]).toMatch(/^Compressing objects:\s+\d+%\s\(\d+\/\d+\),\sdone\./);
// Just to be sure
- expect(readable_stdout[readable_stdout.length - 1]).toMatch(/^.?0{4}/);
+ expect(data_lines[data_lines.length - 1]).toMatch(/^.?0{4}/);
});
});
@@ -318,17 +350,16 @@ describe("API", () => {
it("Should respond with 403", async() => {
expect.assertions(3);
- const res = await app.inject({
- method: "POST",
- url: `${env.AVAIL_REPO}/git-receive-pack`,
+ const res = await axios.post(`/${env.AVAIL_REPO}/git-receive-pack`, null, {
headers: {
- "content-type": "application/x-git-receive-pack-request"
- }
+ "Content-Type": "application/x-git-receive-pack-request"
+ },
+ validateStatus: () => true
});
expect(res).toBeDefined();
- expect(res).toHaveProperty("statusCode");
- expect(res.statusCode).toEqual(403);
+ expect(res).toHaveProperty("status");
+ expect(res.status).toEqual(403);
});
});
});
diff --git a/test/unit/tree.unit.test.ts b/test/unit/tree.unit.test.ts
index 47a716d..d34c93e 100644
--- a/test/unit/tree.unit.test.ts
+++ b/test/unit/tree.unit.test.ts
@@ -1,8 +1,9 @@
import { Repository } from "server/src/git/repository";
import { Tree } from "server/src/git/tree";
-import { TreeEntry } from "server/src/git/tree_entry";
+import { BaseTreeEntry, BlobTreeEntry, TreeEntry } from "server/src/git/tree_entry";
import { BaseError } from "server/src/git/error";
import { EnvironmentVariables } from "../util";
+import { extract, Headers } from "tar-stream";
const env = process.env as EnvironmentVariables;
@@ -34,7 +35,7 @@ describe("Tree", () => {
for(const entry of entries) {
expect(entry).toBeDefined();
- expect(entry).toBeInstanceOf(TreeEntry);
+ expect(entry).toBeInstanceOf(BaseTreeEntry);
}
});
@@ -64,5 +65,60 @@ describe("Tree", () => {
await expect(tree.findExists("packages/core/main.js")).resolves.toBeFalsy();
});
+
+ it("Should create an archive", async() => {
+ const archive = await tree.createArchive();
+
+ expect(archive).toBeDefined();
+
+ const extract_archive = extract();
+
+ archive.pipe(extract_archive);
+
+ type Entry = {
+ header: Headers,
+ content: Buffer[]
+ };
+
+ // Extract the archive entries to an array of entries
+ const entries = await new Promise((resolve: (value: Entry[]) => void) => {
+ const entries = [];
+
+ extract_archive.on("finish", () => {
+ resolve(entries);
+ });
+
+ extract_archive.on("entry", (header, stream, next) => {
+ const content: Buffer[] = [];
+
+ stream.on("data", (chunk: Buffer) => {
+ content.push(chunk);
+ });
+
+ stream.on("end", async() => {
+ entries.push({ header, content });
+ next();
+ });
+
+ stream.resume();
+ });
+ });
+
+ expect(entries).toBeDefined();
+ expect(entries.length).toBeGreaterThan(1);
+
+ for(const entry of entries) {
+ expect(entry).toBeDefined();
+ expect(entry.content).toBeDefined();
+
+ const content = entry.content.join().toString();
+
+ // Get the file content directly
+ const tree_entry = await tree.find(entry.header.name.split("/").slice(1).join("/")) as BlobTreeEntry;
+ const file_content = await (await tree_entry.blob()).content();
+
+ expect(content).toEqual(file_content);
+ }
+ });
});
}); \ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index c89d63e..0f75afa 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2776,6 +2776,13 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
+axios@^0.21.1:
+ version "0.21.1"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
+ integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
+ dependencies:
+ follow-redirects "^1.10.0"
+
babel-jest@^27.0.6:
version "27.0.6"
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.0.6.tgz#e99c6e0577da2655118e3608b68761a5a69bd0d8"
@@ -5126,7 +5133,7 @@ flatted@^3.1.0:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.1.tgz#bbef080d95fca6709362c73044a1634f7c6e7d05"
integrity sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==
-follow-redirects@^1.0.0:
+follow-redirects@^1.0.0, follow-redirects@^1.10.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==