diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/int/api.int.test.ts | 237 | ||||
-rw-r--r-- | test/unit/tree.unit.test.ts | 60 |
2 files changed, 192 insertions, 105 deletions
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 |