in scripts/storage-emulator-integration/tests.ts [158:573]
describe("Admin SDK Endpoints", function (this) {
// eslint-disable-next-line @typescript-eslint/no-invalid-this
this.timeout(TEST_SETUP_TIMEOUT);
let testBucket: Bucket;
before(async () => {
if (!TEST_CONFIG.useProductionServers) {
process.env.STORAGE_EMULATOR_HOST = STORAGE_EMULATOR_HOST;
test = new TriggerEndToEndTest(FIREBASE_PROJECT, __dirname, emulatorConfig);
await test.startEmulators(["--only", "auth,storage"]);
}
// TODO: We should not need a real credential for emulator tests, but
// today we do.
const credential = fs.existsSync(path.join(__dirname, SERVICE_ACCOUNT_KEY))
? admin.credential.cert(readJson(SERVICE_ACCOUNT_KEY))
: admin.credential.applicationDefault();
admin.initializeApp({
credential,
});
testBucket = admin.storage().bucket(storageBucket);
smallFilePath = createRandomFile("small_file", SMALL_FILE_SIZE);
largeFilePath = createRandomFile("large_file", LARGE_FILE_SIZE);
});
beforeEach(async () => {
if (!TEST_CONFIG.useProductionServers) {
await resetStorageEmulator(STORAGE_EMULATOR_HOST);
} else {
await testBucket.deleteFiles();
}
});
describe(".bucket()", () => {
describe("#upload()", () => {
it("should handle non-resumable uploads", async () => {
await testBucket.upload(smallFilePath, {
resumable: false,
});
// Doesn't require an assertion, will throw on failure
});
it("should replace existing file on upload", async () => {
const path = "replace.txt";
const content1 = createRandomFile("small_content_1", 10);
const content2 = createRandomFile("small_content_2", 10);
const file = testBucket.file(path);
await testBucket.upload(content1, {
destination: path,
});
const [readContent1] = await file.download();
expect(readContent1).to.deep.equal(fs.readFileSync(content1));
await testBucket.upload(content2, {
destination: path,
});
const [readContent2] = await file.download();
expect(readContent2).to.deep.equal(fs.readFileSync(content2));
fs.unlinkSync(content1);
fs.unlinkSync(content2);
});
it("should handle gzip'd uploads", async () => {
// This appears to pass, but the file gets corrupted cause it's gzipped?
// expect(true).to.be.false;
await testBucket.upload(smallFilePath, {
gzip: true,
});
});
// TODO(abehaskins): This test is temporarily disabled due to a credentials issue
it.skip("should handle large (resumable) uploads", async () => {
await testBucket.upload(largeFilePath),
{
resumable: true,
};
});
});
describe("#getFiles()", () => {
it("should list files", async () => {
await testBucket.upload(smallFilePath, {
destination: "testing/shoveler.svg",
});
const [files, prefixes] = await testBucket.getFiles({
directory: "testing",
});
expect(prefixes).to.be.undefined;
expect(files.map((file) => file.name)).to.deep.equal(["testing/shoveler.svg"]);
});
});
});
describe(".file()", () => {
describe("#save()", () => {
// TODO(abehaskins): This test is temporarily disabled due to a credentials issue
it.skip("should accept a zero-byte file", async () => {
await testBucket.file("testing/dir/").save("");
const [files] = await testBucket.getFiles({
directory: "testing",
});
expect(files.map((file) => file.name)).to.contain("testing/dir/");
});
});
describe("#get()", () => {
// TODO(abehaskins): This test is temporarily disabled due to a credentials issue
it.skip("should complete an save/get/download cycle", async () => {
const p = "testing/dir/hello.txt";
const content = "hello, world";
await testBucket.file(p).save(content);
const [f] = await testBucket.file(p).get();
const [buf] = await f.download();
expect(buf.toString()).to.equal(content);
});
});
describe("#delete()", () => {
it("should properly delete a file from the bucket", async () => {
// We use a nested path to ensure that we don't need to decode
// the objectId in the gcloud emulator API
const bucketFilePath = "file/to/delete";
await testBucket.upload(smallFilePath, {
destination: bucketFilePath,
});
// Get a reference to the uploaded file
const toDeleteFile = testBucket.file(bucketFilePath);
// Ensure that the file exists on the bucket before deleting it
const [existsBefore] = await toDeleteFile.exists();
expect(existsBefore).to.equal(true);
// Delete it
await toDeleteFile.delete();
// Ensure that it doesn't exist anymore on the bucket
const [existsAfter] = await toDeleteFile.exists();
expect(existsAfter).to.equal(false);
});
});
describe("#download()", () => {
it("should return the content of the file", async () => {
await testBucket.upload(smallFilePath);
const [downloadContent] = await testBucket
.file(smallFilePath.split("/").slice(-1)[0])
.download();
const actualContent = fs.readFileSync(smallFilePath);
expect(downloadContent).to.deep.equal(actualContent);
});
});
describe("#makePublic()", () => {
it("should no-op", async () => {
const destination = "a/b";
await testBucket.upload(smallFilePath, { destination });
const [aclMetadata] = await testBucket.file(destination).makePublic();
const generation = aclMetadata.generation;
delete aclMetadata.generation;
expect(aclMetadata).to.deep.equal({
kind: "storage#objectAccessControl",
object: destination,
id: `${testBucket.name}/${destination}/${generation}/allUsers`,
selfLink: `${STORAGE_EMULATOR_HOST}/storage/v1/b/${
testBucket.name
}/o/${encodeURIComponent(destination)}/acl/allUsers`,
bucket: testBucket.name,
entity: "allUsers",
role: "READER",
etag: "someEtag",
});
});
it("should not interfere with downloading of bytes via public URL", async () => {
const destination = "a/b";
await testBucket.upload(smallFilePath, { destination });
await testBucket.file(destination).makePublic();
const publicLink = `${STORAGE_EMULATOR_HOST}/${testBucket.name}/${destination}`;
const requestClient = TEST_CONFIG.useProductionServers ? https : http;
await new Promise((resolve, reject) => {
requestClient.get(publicLink, {}, (response) => {
const data: any = [];
response
.on("data", (chunk) => data.push(chunk))
.on("end", () => {
expect(Buffer.concat(data).length).to.equal(SMALL_FILE_SIZE);
})
.on("close", resolve)
.on("error", reject);
});
});
});
});
describe("#getMetadata()", () => {
it("should throw on non-existing file", async () => {
let err: any;
await testBucket
.file(smallFilePath)
.getMetadata()
.catch((_err) => {
err = _err;
});
expect(err).to.not.be.empty;
});
it("should return generated metadata for new upload", async () => {
await testBucket.upload(smallFilePath);
const [metadata] = await testBucket
.file(smallFilePath.split("/").slice(-1)[0])
.getMetadata();
const metadataTypes: { [s: string]: string } = {};
for (const key in metadata) {
if (metadata[key]) {
metadataTypes[key] = typeof metadata[key];
}
}
expect(metadataTypes).to.deep.equal({
bucket: "string",
contentType: "string",
generation: "string",
md5Hash: "string",
crc32c: "string",
etag: "string",
metageneration: "string",
storageClass: "string",
name: "string",
size: "string",
timeCreated: "string",
updated: "string",
id: "string",
kind: "string",
mediaLink: "string",
selfLink: "string",
timeStorageClassUpdated: "string",
});
});
it("should return a functional media link", async () => {
await testBucket.upload(smallFilePath);
const [{ mediaLink }] = await testBucket
.file(smallFilePath.split("/").slice(-1)[0])
.getMetadata();
const requestClient = TEST_CONFIG.useProductionServers ? https : http;
await new Promise((resolve, reject) => {
requestClient.get(mediaLink, {}, (response) => {
const data: any = [];
response
.on("data", (chunk) => data.push(chunk))
.on("end", () => {
expect(Buffer.concat(data).length).to.equal(SMALL_FILE_SIZE);
})
.on("close", resolve)
.on("error", reject);
});
});
});
it("should handle firebaseStorageDownloadTokens", async () => {
const destination = "public/small_file";
await testBucket.upload(smallFilePath, {
destination,
metadata: {},
});
const cloudFile = testBucket.file(destination);
const md = {
metadata: {
firebaseStorageDownloadTokens: "myFirstToken,mySecondToken",
},
};
await cloudFile.setMetadata(md);
// Check that the tokens are saved in Firebase metadata
await supertest(STORAGE_EMULATOR_HOST)
.get(`/v0/b/${testBucket.name}/o/${encodeURIComponent(destination)}`)
.expect(200)
.then((res) => {
const firebaseMd = res.body;
expect(firebaseMd.downloadTokens).to.equal(md.metadata.firebaseStorageDownloadTokens);
});
// Check that the tokens are saved in Cloud metadata
const [metadata] = await cloudFile.getMetadata();
expect(metadata.metadata.firebaseStorageDownloadTokens).to.deep.equal(
md.metadata.firebaseStorageDownloadTokens
);
});
});
describe("#setMetadata()", () => {
it("should throw on non-existing file", async () => {
let err: any;
await testBucket
.file(smallFilePath)
.setMetadata({ contentType: 9000 })
.catch((_err) => {
err = _err;
});
expect(err).to.not.be.empty;
});
it("should allow overriding of default metadata", async () => {
await testBucket.upload(smallFilePath);
const [metadata] = await testBucket
.file(smallFilePath.split("/").slice(-1)[0])
.setMetadata({ contentType: "very/fake" });
const metadataTypes: { [s: string]: string } = {};
for (const key in metadata) {
if (metadata[key]) {
metadataTypes[key] = typeof metadata[key];
}
}
expect(metadata.contentType).to.equal("very/fake");
expect(metadataTypes).to.deep.equal({
bucket: "string",
contentType: "string",
generation: "string",
md5Hash: "string",
crc32c: "string",
etag: "string",
metageneration: "string",
storageClass: "string",
name: "string",
size: "string",
timeCreated: "string",
updated: "string",
id: "string",
kind: "string",
mediaLink: "string",
selfLink: "string",
timeStorageClassUpdated: "string",
});
});
it("should allow setting of optional metadata", async () => {
await testBucket.upload(smallFilePath);
const [metadata] = await testBucket
.file(smallFilePath.split("/").slice(-1)[0])
.setMetadata({ cacheControl: "no-cache", contentLanguage: "en" });
const metadataTypes: { [s: string]: string } = {};
for (const key in metadata) {
if (metadata[key]) {
metadataTypes[key] = typeof metadata[key];
}
}
expect(metadata.cacheControl).to.equal("no-cache");
expect(metadata.contentLanguage).to.equal("en");
});
it("should allow fields under .metadata", async () => {
await testBucket.upload(smallFilePath);
const [metadata] = await testBucket
.file(smallFilePath.split("/").slice(-1)[0])
.setMetadata({ metadata: { is_over: "9000" } });
expect(metadata.metadata.is_over).to.equal("9000");
});
it("should ignore any unknown fields", async () => {
await testBucket.upload(smallFilePath);
const [metadata] = await testBucket
.file(smallFilePath.split("/").slice(-1)[0])
.setMetadata({ nada: "true" });
expect(metadata.nada).to.be.undefined;
});
});
});
after(async () => {
if (tmpDir) {
fs.unlinkSync(smallFilePath);
fs.unlinkSync(largeFilePath);
fs.rmdirSync(tmpDir);
}
if (!TEST_CONFIG.useProductionServers) {
delete process.env.STORAGE_EMULATOR_HOST;
await test.stopEmulators();
}
});
});