Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions src/deploy/hosting/uploader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { expect } from "chai";
import * as sinon from "sinon";
import * as fs from "fs";
import * as zlib from "zlib";
import { Uploader } from "./uploader";
import { Client } from "../../apiv2";
import * as hashcache from "./hashcache";

describe("deploy/hosting/uploader", () => {
let clientPostStub: sinon.SinonStub;
let clientRequestStub: sinon.SinonStub;

class MockQueue {
public handler: any;

Check warning on line 14 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
private promises: Promise<void>[] = [];
constructor(options: any) {

Check warning on line 16 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
this.handler = options.handler;

Check warning on line 17 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .handler on an `any` value

Check warning on line 17 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe assignment of an `any` value
}
add(item: any) {

Check warning on line 19 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type

Check warning on line 19 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
const p = Promise.resolve(this.handler(item));

Check warning on line 20 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe call of an `any` typed value
this.promises.push(p);

Check warning on line 21 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe argument of type `Promise<any>` assigned to a parameter of type `Promise<void>`
}
process() { }

Check failure on line 23 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Delete `·`

Check failure on line 23 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Unexpected empty method 'process'

Check failure on line 23 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Delete `·`

Check failure on line 23 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected empty method 'process'

Check warning on line 23 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function

Check failure on line 23 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Delete `·`

Check failure on line 23 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Unexpected empty method 'process'
async wait() {

Check warning on line 24 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Missing return type on function
await Promise.all(this.promises);
return Promise.resolve();
}
close() { }

Check failure on line 28 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Delete `·`

Check failure on line 28 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Unexpected empty method 'close'

Check failure on line 28 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Delete `·`

Check failure on line 28 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected empty method 'close'

Check failure on line 28 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Delete `·`

Check failure on line 28 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Unexpected empty method 'close'
stats() {
return { total: 0, complete: 0, cursor: 0 };
}
}
Comment on lines +13 to +32
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The MockQueue class uses any in its constructor and methods. It could be made more type-safe by using generics, similar to the actual Queue implementation it's mocking. This would improve the robustness and readability of the test setup. After applying this change, you'll also need to update the instantiation of MockQueue to provide the generic type, for example: new MockQueue<string>({ ... }).

Suggested change
class MockQueue {
public handler: any;
private promises: Promise<void>[] = [];
constructor(options: any) {
this.handler = options.handler;
}
add(item: any) {
const p = Promise.resolve(this.handler(item));
this.promises.push(p);
}
process() { }
async wait() {
await Promise.all(this.promises);
return Promise.resolve();
}
close() { }
stats() {
return { total: 0, complete: 0, cursor: 0 };
}
}
class MockQueue<T> {
public handler: (item: T) => Promise<void>;
private promises: Promise<void>[] = [];
constructor(options: { handler: (item: T) => Promise<void> }) {
this.handler = options.handler;
}
add(item: T) {
const p = Promise.resolve(this.handler(item));
this.promises.push(p);
}
process() { }
async wait() {
await Promise.all(this.promises);
return Promise.resolve();
}
close() { }
stats() {
return { total: 0, complete: 0, cursor: 0 };
}
}


beforeEach(() => {
sinon.stub(fs, "statSync");
sinon.stub(fs, "createReadStream");
sinon.stub(zlib, "createGzip");
clientPostStub = sinon.stub(Client.prototype, "post");
clientRequestStub = sinon.stub(Client.prototype, "request");
sinon.stub(hashcache, "load").returns(new Map());
sinon.stub(hashcache, "dump");
});

afterEach(() => {
sinon.restore();
});

it("should initialize correctly", () => {
const uploader = new Uploader({
version: "v1",
projectRoot: "root",
files: ["file1.txt"],
public: "public",
});
expect(uploader).to.be.instanceOf(Uploader);
});

it("should hash files and populate version", async () => {
const uploader = new Uploader({
version: "v1",
projectRoot: "root",
files: ["file1.txt", "file2.txt"],
public: "public",
});
(uploader as any).hashQueue = new MockQueue({ handler: (uploader as any).hashHandler.bind(uploader) });

Check failure on line 65 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Replace `·handler:·(uploader·as·any).hashHandler.bind(uploader)` with `⏎······handler:·(uploader·as·any).hashHandler.bind(uploader),⏎···`

Check failure on line 65 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Replace `·handler:·(uploader·as·any).hashHandler.bind(uploader)` with `⏎······handler:·(uploader·as·any).hashHandler.bind(uploader),⏎···`

Check failure on line 65 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Replace `·handler:·(uploader·as·any).hashHandler.bind(uploader)` with `⏎······handler:·(uploader·as·any).hashHandler.bind(uploader),⏎···`
(uploader as any).populateQueue = new MockQueue({ handler: (uploader as any).populateHandler.bind(uploader) });

Check failure on line 66 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Replace `·handler:·(uploader·as·any).populateHandler.bind(uploader)` with `⏎······handler:·(uploader·as·any).populateHandler.bind(uploader),⏎···`

Check failure on line 66 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Replace `·handler:·(uploader·as·any).populateHandler.bind(uploader)` with `⏎······handler:·(uploader·as·any).populateHandler.bind(uploader),⏎···`

Check failure on line 66 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Replace `·handler:·(uploader·as·any).populateHandler.bind(uploader)` with `⏎······handler:·(uploader·as·any).populateHandler.bind(uploader),⏎···`
(uploader as any).uploadQueue = new MockQueue({ handler: (uploader as any).uploadHandler.bind(uploader) });

Check failure on line 67 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Replace `·handler:·(uploader·as·any).uploadHandler.bind(uploader)` with `⏎······handler:·(uploader·as·any).uploadHandler.bind(uploader),⏎···`

Check failure on line 67 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Replace `·handler:·(uploader·as·any).uploadHandler.bind(uploader)` with `⏎······handler:·(uploader·as·any).uploadHandler.bind(uploader),⏎···`

Check failure on line 67 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Replace `·handler:·(uploader·as·any).uploadHandler.bind(uploader)` with `⏎······handler:·(uploader·as·any).uploadHandler.bind(uploader),⏎···`

(fs.statSync as sinon.SinonStub).returns({ mtime: new Date(), size: 100 });
const { PassThrough, Readable } = require("stream");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For consistency with other imports and better code organization, please use an import statement at the top of the file instead of require() within the test. You can remove this line and add import { PassThrough, Readable } from "stream"; to the top of the file.


// Mock stream for file1.txt
const mockStream1 = new Readable({

Check failure on line 73 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

'mockStream1' is assigned a value but never used

Check failure on line 73 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

'mockStream1' is assigned a value but never used

Check failure on line 73 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

'mockStream1' is assigned a value but never used
read() {
this.push(Buffer.from("hash1"));
this.push(null);
},
});
// Mock stream for file2.txt
const mockStream2 = new Readable({

Check failure on line 80 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

'mockStream2' is assigned a value but never used

Check failure on line 80 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

'mockStream2' is assigned a value but never used

Check failure on line 80 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

'mockStream2' is assigned a value but never used
read() {
this.push(Buffer.from("hash2"));
this.push(null);
},
});

(zlib.createGzip as sinon.SinonStub).callsFake(() => new PassThrough());
(fs.createReadStream as sinon.SinonStub).callsFake((filePath: string) => {
if (filePath.includes("file1.txt")) {
return new Readable({
read() {
this.push(Buffer.from("hash1"));
this.push(null);
},
});
}
if (filePath.includes("file2.txt")) {
return new Readable({
read() {
this.push(Buffer.from("hash2"));
this.push(null);
},
});
}
return new PassThrough();
});
Comment on lines +72 to +106
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This section for mocking streams can be simplified. The mockStream1 and mockStream2 variables are declared but never used and can be removed. Additionally, the Readable stream creation logic is duplicated inside fs.createReadStream.callsFake. This can be extracted to a helper function to make the code cleaner and more maintainable.

    const createMockStream = (content: string) =>
      new Readable({
        read() {
          this.push(Buffer.from(content));
          this.push(null);
        },
      });

    (zlib.createGzip as sinon.SinonStub).callsFake(() => new PassThrough());
    (fs.createReadStream as sinon.SinonStub).callsFake((filePath: string) => {
      if (filePath.includes("file1.txt")) {
        return createMockStream("hash1");
      }
      if (filePath.includes("file2.txt")) {
        return createMockStream("hash2");
      }
      return new PassThrough();
    });


clientPostStub.resolves({
body: {
uploadUrl: "https://upload.url",
uploadRequiredHashes: [
"af316ecb91a8ee7ae99210702b2d4758f30cdde3bf61e3d8e787d74681f90a6e", // hash for "hash1"
"e7bf382f6e5915b3f88619b866223ebf1d51c4c5321cccde2e9ff700a3259086" // hash for "hash2"

Check failure on line 113 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Replace `·` with `,`

Check failure on line 113 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Replace `·` with `,`

Check failure on line 113 in src/deploy/hosting/uploader.spec.ts

View workflow job for this annotation

GitHub Actions / unit (24)

Replace `·` with `,`
]
},
});
clientRequestStub.resolves({ status: 200, response: { text: sinon.stub().resolves("") } });

await uploader.start();

expect(clientPostStub.calledWithMatch(/\/v1:populateFiles/)).to.be.true;
expect(clientPostStub.firstCall.args[1].files).to.have.property("/file1.txt");
expect(clientPostStub.firstCall.args[1].files).to.have.property("/file2.txt");
expect(clientRequestStub.calledTwice).to.be.true;
});
});
Loading