Skip to content
Merged
Show file tree
Hide file tree
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
169 changes: 169 additions & 0 deletions tests/functional/raw-node/test/GCP/bucket/bucket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
const assert = require('assert');
const async = require('async');
const util = require('util');
const arsenal = require('arsenal');
const {
HeadBucketCommand,
ListObjectsCommand,
CreateBucketCommand,
DeleteBucketCommand,
PutObjectCommand,
DeleteObjectCommand,
} = require('@aws-sdk/client-s3');
const { GCP } = arsenal.storage.data.external.GCP;
const { genUniqID, genBucketName, gcpRetry } = require('../../../utils/gcpUtils');
const { getRealAwsConfig } =
require('../../../../aws-node-sdk/test/support/awsConfig');
const { listingHardLimit } = require('../../../../../../constants');

const credentialOne = 'gcpbackend';
const config = getRealAwsConfig(credentialOne);
const gcpClient = new GCP(config);

describe('GCP: Bucket', function testSuite() {
this.timeout(180000);

const bucketName = genBucketName('bucket');

before(async () => {
process.stdout.write(`Creating test bucket ${bucketName}\n`);
await gcpRetry(gcpClient, new CreateBucketCommand({ Bucket: bucketName }));
});

after(async () => {
await gcpRetry(gcpClient, new DeleteBucketCommand({ Bucket: bucketName }));
});

describe('HEAD Bucket', () => {
it('should return 404 for non-existing bucket', async () => {
const badBucketName = `cldsrvci-bucket-${genUniqID()}`;
try {
await gcpClient.send(new HeadBucketCommand({ Bucket: badBucketName }));
assert.fail('Expected 404 error, but got success');
} catch (err) {
assert(err);
assert.strictEqual(err.$metadata?.httpStatusCode, 404);
const errorName = err.name === 'NotFound' ? 'NoSuchBucket' : err.name;
assert.strictEqual(errorName, 'NoSuchBucket');
}
});

it('should return 200 and bucket metadata', async () => {
// Need to use the helper headBucket function for middleware with MetaVersionId
const res = await util.promisify(gcpClient.headBucket.bind(gcpClient))({ Bucket: bucketName });
const { $metadata, ...data } = res;
assert.strictEqual($metadata?.httpStatusCode, 200);
// Ensure MetaVersionId is present and non-empty
assert.ok(
typeof data.MetaVersionId === 'string'
&& data.MetaVersionId.length > 0
);
});
});

describe('GET Bucket (List Objects)', () => {
const smallSize = 20;
const bigSize = listingHardLimit + 1;

function populateBucket(createdObjects, callback) {
process.stdout.write(
`Putting ${createdObjects.length} objects into bucket\n`);
async.mapLimit(
createdObjects,
10,
async object => gcpClient.send(new PutObjectCommand({
Copy link

Choose a reason for hiding this comment

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

The populateBucket and removeObjects helpers call gcpClient.send() directly without gcpRetry. If a PutObjectCommand or DeleteObjectCommand hits a SlowDown/429 during setup/teardown, the test will still fail. Consider wrapping these calls with gcpRetry to match the PR goal of fixing rate-limit flakiness.

--- Claude Code

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a copy of existing code to another file

Bucket: bucketName,
Key: object,
})),
err => {
if (err) {
process.stdout.write(`err putting objects ${err}\n`);
}
return callback(err);
}
);
}

function removeObjects(createdObjects, callback) {
process.stdout.write(
`Deleting ${createdObjects.length} objects from bucket\n`);
async.mapLimit(
createdObjects,
10,
async object => gcpClient.send(new DeleteObjectCommand({
Bucket: bucketName,
Key: object,
})),
err => {
if (err) {
process.stdout.write(`err deleting objects ${err}\n`);
}
return callback(err);
}
);
}

it('should return 200', async () => {
const res = await gcpClient.send(
new ListObjectsCommand({ Bucket: bucketName }));
assert.strictEqual(res.$metadata?.httpStatusCode, 200);
});

describe('with less than listingHardLimit number of objects', () => {
const createdObjects = Array.from(
Array(smallSize).keys()).map(i => `someObject-${i}`);

before(done => populateBucket(createdObjects, done));
after(done => removeObjects(createdObjects, done));

it(`should list all ${smallSize} created objects`, async () => {
const res = await gcpClient.send(
new ListObjectsCommand({ Bucket: bucketName }));
assert.strictEqual(res.Contents.length, smallSize);
});

it('should list MaxKeys number of objects with MaxKeys at 10', async () => {
const res = await gcpClient.send(new ListObjectsCommand({
Bucket: bucketName,
MaxKeys: 10,
}));
assert.strictEqual(res.Contents.length, 10);
});
});

describe('with more than listingHardLimit number of objects', () => {
const createdObjects = Array.from(
Array(bigSize).keys()).map(i => `someObject-${i}`);

before(done => populateBucket(createdObjects, done));
after(done => removeObjects(createdObjects, done));

it('should list at max 1000 of objects created', async () => {
const res = await gcpClient.send(
new ListObjectsCommand({ Bucket: bucketName }));
assert.strictEqual(res.Contents.length, listingHardLimit);
});

describe('with MaxKeys at 1001', () => {
// TODO: S3C-5445
// Note: this test is testing GCP behaviour, not the Cloudserver one.
// It tests that GET https://<GCP_BUCKET_NAME>.storage.googleapis.com/?max-keys=1001
// returns only the first 1000 objects.
//
// Expected behavior: the GCP XML API should not return a list longer
// than 1000 objects, even if max-keys is greater than 1000:
// https://cloud.google.com/storage/docs/xml-api/reference-headers#maxkeys
//
// Actual behavior: it returns a list longer than 1000 objects when
// max-keys is greater than 1000
it.skip('should list at max 1000, ignoring MaxKeys', async () => {
const res = await gcpClient.send(new ListObjectsCommand({
Bucket: bucketName,
MaxKeys: 1001,
}));
assert.strictEqual(res.Contents.length, listingHardLimit);
});
});
});
});
});
183 changes: 0 additions & 183 deletions tests/functional/raw-node/test/GCP/bucket/get.js

This file was deleted.

Loading
Loading