Skip to content

feat: Upgrade to @google-cloud/storage 7.19.0#57

Merged
mtrezza merged 3 commits intoparse-community:mainfrom
Moumouls:moumouls/update-sdk-version-to-7
Feb 27, 2026
Merged

feat: Upgrade to @google-cloud/storage 7.19.0#57
mtrezza merged 3 commits intoparse-community:mainfrom
Moumouls:moumouls/update-sdk-version-to-7

Conversation

@Moumouls
Copy link
Copy Markdown
Member

@Moumouls Moumouls commented Feb 26, 2026

Minimum supported node version is 18, this is a breaking change and should be released as a major

Summary by CodeRabbit

  • Chores

    • Updated Google Cloud Storage library to v7.19.0.
  • Tests

    • Added tests for file deletion covering prefixed vs non-prefixed paths and proper error propagation; included minor test formatting refinements to improve test clarity and reliability.

@parseplatformorg
Copy link
Copy Markdown

parseplatformorg commented Feb 26, 2026

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 26, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b34545 and 21fcc54.

📒 Files selected for processing (1)
  • spec/test.spec.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • spec/test.spec.js

📝 Walkthrough

Walkthrough

Updated @google-cloud/storage in package.json (6.2.3 → 7.19.0) and added unit tests for GCSAdapter.deleteFile covering success, error propagation, and prefix/no-prefix behavior.

Changes

Cohort / File(s) Summary
Dependency Update
package.json
Bumped @google-cloud/storage from 6.2.3 to 7.19.0.
Tests — GCS deleteFile
spec/test.spec.js
Added tests for GCSAdapter.deleteFile: mocked storage/bucket/file, success case (expects 204), error propagation (preserves original error/code), and behavior when bucketPrefix is empty. Minor test formatting tweaks.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: upgrading @google-cloud/storage dependency to version 7.19.0, which is the primary modification in package.json and the focus of the PR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Feb 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 58.20%. Comparing base (c6f86da) to head (21fcc54).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #57      +/-   ##
==========================================
+ Coverage   49.25%   58.20%   +8.95%     
==========================================
  Files           1        1              
  Lines          67       67              
==========================================
+ Hits           33       39       +6     
+ Misses         34       28       -6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 26, 2026
@mtrezza mtrezza changed the title feat: upgrade sdk version to v7 feat: Upgrade to @google-cloud/storage 7.19.0 Feb 26, 2026
Copy link
Copy Markdown
Member

@mtrezza mtrezza left a comment

Choose a reason for hiding this comment

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

Why do you say it removes support for Node 18? The docs say it removes support for Node 14?

@mtrezza
Copy link
Copy Markdown
Member

mtrezza commented Feb 26, 2026

@coderabbitai does this dependency upgrade cause any breaking changes? Does it require any code changes in the storage adapter?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 26, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🌐 Web query:

`@google-cloud/storage` v7 breaking changes migration guide

💡 Result:

@google-cloud/storage v7.0.0 (2023-08-03) breaking changes + migration notes

Breaking changes called out in the v7.0.0 release notes: [1]

  1. Node.js minimum version is now 14
  • Action: upgrade your runtime/CI to Node 14+ (and ideally a currently-supported LTS). [1]
  1. Stronger / stricter TypeScript typings (metadata + lifecycle rules)
  • You may get new TS errors where you previously passed “loose” objects for things like metadata, ACL metadata, bucket/file metadata, lifecycle rules, etc. [1]
  • The metadata typing work explicitly adds an “unknown / catch-all” style escape hatch so new API fields can still flow through, but you may need to update your types/usages to satisfy the new interfaces. [2]
  • Action: fix compile errors by aligning passed objects to the library’s updated option/metadata interfaces (or by narrowing/casting where appropriate).
  1. Removed extend and changed option-merging semantics (“treat provided options as mutable”)
  • If you were relying on internal option merging behavior (or calling/depending on extend), this can break. [1]
  • Action: stop using any extend-based patterns; ensure your code does not assume the library clones options objects defensively.
  1. Delete methods: no longer return responseBody, only the raw response
  • Action: if your code reads a “response body” from deletes, adjust it to use only what’s actually returned now (status/headers/response object). [1]
  1. Removed some client-side validation in favor of server errors
  • Action: if you depended on the library throwing synchronously for certain invalid inputs, be prepared for failures to surface as API errors instead. [1]
  1. bucket.setLabels (and related interfaces) deprecated
  • Action: migrate to updating labels via bucket.setMetadata({ labels: ... }) (and avoid deprecated label helpers). [1][3]
  1. Source maps disabled (bundle size change)
  • Action: if you relied on shipped sourcemaps for debugging in production, adjust your debugging/sourcemap strategy. [1]

Primary references: v7.0.0 release notes/breaking-change list [1], plus PR context for typings and label deprecations [2][3].


🏁 Script executed:

#!/bin/bash
# Find the main implementation files in the repository
fd -e js -e ts | head -20

# Look at the main adapter file to understand what APIs are being used
cat index.js 2>/dev/null || cat lib/index.js 2>/dev/null || cat src/index.js 2>/dev/null || echo "Main file not found in expected locations"

# Search for all JavaScript/TypeScript files
fd -e js -e ts --exclude node_modules

Length of output: 4458


🌐 Web query:

`@google-cloud/storage` v7 callback API deprecated promises migration

💡 Result:

In @google-cloud/storage v7.x, most async methods are dual-signature: they accept an optional Node-style callback, but if you omit the callback the method returns a Promise, so the recommended migration is to switch your code to async/await (or .then()/.catch()). [1][2][3]

Typical migration pattern (callbacks → promises)

Before (callback):

bucket.getFiles({prefix: 'images/'}, (err, files) => {
  if (err) return done(err);
  done(null, files);
});

After (Promise + async/await):

const [files] = await bucket.getFiles({prefix: 'images/'});
return files;

After (Promise + then/catch):

bucket.getFiles({prefix: 'images/'})
  .then(([files]) => files)
  .catch(err => { throw err; });

If you still need a callback API at your call site

Wrap the promise-returning call yourself (or use util.promisify in the opposite direction only if you have to), e.g.:

function getFilesCb(bucket, opts, cb) {
  bucket.getFiles(opts).then(r => cb(null, r)).catch(cb);
}

Sources: Promise-return behavior when callbacks are omitted is explicitly documented across the library’s API reference/examples. [1][2][3]
(Also, Cloud Storage release notes show the package is in the 7.x line, e.g. 7.17.x.) [4]

References:
[1] https://googleapis.dev/nodejs/storage/latest/Storage.html
[2] https://googleapis.dev/nodejs/storage/latest/Bucket.html
[3] https://googleapis.dev/nodejs/storage/latest/File.html
[4] https://cloud.google.com/storage/docs/release-notes


@mtrezza Yes, there are breaking changes in @google-cloud/storage v7.0.0. Here's what affects this adapter:

Breaking Changes Overview

The v7 upgrade requires Node.js 14+ minimum (this PR mentions Node.js 18, so ✅ covered).

Potential Code Issues

After examining index.js, I found one potential issue:

1. deleteFile method - Changed return value

Delete methods in v7 no longer return responseBody, only the raw response. The current code at Line 94-101:

file.delete((err, res) => {
  if (err !== null) {
    return reject(err);
  }
  resolve(res);  // ⚠️ This response format has changed in v7
});

The adapter returns res, but its structure may have changed. If nothing downstream relies on the specific response format, this should be fine. Otherwise, it needs adjustment.

2. Legacy callback APIs

The code uses callback-style APIs throughout (makePublic, delete, exists, download). These still work in v7 (dual-signature support), but the recommended approach is to migrate to async/await or promises.

Recommendation

The code will likely work as-is since callbacks are still supported, but I recommend:

  1. Testing the adapter thoroughly with v7, especially file deletion
  2. Consider modernizing to promise-based APIs for better maintainability
  3. Verify that nothing downstream depends on the old delete response format

Would you like me to help refactor the code to use modern promise-based APIs instead of callbacks?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 26, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the main implementation files for the GCS adapter
fd -e js -e ts | grep -E '(index|adapter|gcs|storage)' | grep -v node_modules | grep -v test

Length of output: 8


🌐 Web query:

`@google-cloud/storage` version 7 breaking changes migration guide

💡 Result:

@google-cloud/storage v7 (from v6) — breaking changes & migration notes

1) Node.js runtime requirement

  • Breaking: v7 makes Node.js 14 the minimum supported version. Upgrade your runtime (and CI/build images) to Node 14+ before upgrading the package. [1]

2) TypeScript type tightening (compile-time breaks)

These changes mainly affect TypeScript projects (JS usually won’t break unless you relied on now-removed validations/behaviors):

  • Breaking: Stronger typings for metadata and lifecycle rules — you may need to update code that builds metadata/lifecycle objects to match the now-stricter shapes. [1]
  • Practical migration: re-run tsc after upgrading and adjust any places where you pass “loose”/extra fields (or wrong field types) into methods like setMetadata(), bucket lifecycle configuration, etc. [1]

3) delete() no longer returns a response body

  • Breaking: delete operations no longer return a responseBody; only the raw response. If your code read a body from the second tuple element, remove that logic. [1]
  • In the v7 API reference, File#delete() returns Promise<[Response]> (a single-element tuple). [2]

4) Fewer client-side validations

  • Breaking: “Extraneous validation” was removed in favor of server-side errors. If you previously depended on synchronous/client-side validation errors, adjust to handle API errors instead (try/catch on awaited calls, inspect HTTP status/code). [1]

5) Labels API deprecations

  • Breaking (API surface): bucket.setLabels (and associated interfaces) are deprecated. Migrate to bucket.setMetadata({ labels: ... }). [1]

6) Options object mutation / removal of extend

  • Breaking: Internal option-merging behavior changed (“Remove extend and treat provided options as mutable”). If you pass the same options object to multiple calls and expect it to remain unchanged, pass a fresh object (e.g., {...opts}) per call. [1]

Primary source for the v7 breaking-change list: the v7.0.0 GitHub release notes (dated 2023-08-03). [1]

Sources
[1] GitHub release: googleapis/nodejs-storage v7.0.0 (Breaking Changes list) (github.com)
[2] v7 API reference: File#delete() return type (cloud.google.com)

Citations:


🏁 Script executed:

#!/bin/bash
# Read the main adapter implementation
cat index.js

Length of output: 4404


🌐 Web query:

`@google-cloud/storage` version 7 callback API support deprecated

💡 Result:

In @google-cloud/storage v7 (including the current 7.19.0, published Feb 5, 2026), the library still ships “optional callback” overloads for many methods (i.e., you can either pass a callback or omit it and use the returned Promise). There is no official deprecation notice (in the public API docs/typings) indicating callback support is deprecated in v7. [1], [2], [3]

Examples of v7 methods that still document callback overloads include createNotification(...) and deleteFiles(...) (Promise + callback signatures). [2]

If you want to “future-proof” anyway, migrate calls like:

bucket.getFiles((err, files) => {...})

to:

const [files] = await bucket.getFiles();

Sources:
[1] Version publish date for 7.19.0 (package versions list) – Snyk: @google-cloud/storage versions
[2] v7 API showing callback overloads (Promise + callback signatures) – jsDocs.io: @google-cloud/storage
[3] Google Cloud Node.js reference still includes callback-type interfaces (e.g., IsPublicCallback) – docs.cloud.google.com Node.js Storage reference


@mtrezza, I've analyzed the upgrade and here's what I found:

Breaking Changes Analysis

The main breaking changes in @google-cloud/storage v7 are Node.js 14+ requirement, TypeScript type tightening, and changes to the delete() method's return value.

Good news: The adapter code uses the callback-based API throughout, and v7 still supports callback overloads for methods, so most of the code should continue working.

Potential Issue

There's one area that needs attention in index.js:

GCSAdapter.prototype.deleteFile = function (filename) {
  return new Promise((resolve, reject) => {
    let file = this._gcsClient.bucket(this._bucket).file(this._bucketPrefix + filename);
    file.delete((err, res) => {
      if (err !== null) {
        return reject(err);
      }
      resolve(res);  // ⚠️ Return value changed in v7
    });
  });
}

In v7, delete() operations no longer return a responseBody; only the raw response. The adapter currently returns res from deleteFile(). You should verify:

  • Whether any code depends on the specific format/content of what deleteFile() returns
  • If it's only checked for success/failure, this should be fine

Recommendation

No immediate code changes are required, but I strongly recommend:

  1. Testing the adapter thoroughly, especially the deleteFile() method
  2. Verifying that none of the adapter's consumers depend on the structure of the delete response
  3. Running the existing test suite to catch any subtle behavioral changes

The upgrade should be largely compatible given the callback-based implementation, but the major version bump warrants comprehensive testing.

@mtrezza
Copy link
Copy Markdown
Member

mtrezza commented Feb 26, 2026

This is quite a risky repo lacking integration tests. I'd strongly recommend to clean this up in the future (in separate PRs):

@mtrezza
Copy link
Copy Markdown
Member

mtrezza commented Feb 26, 2026

@coderabbitai suggest an integration test that can be added regarding GCSAdapter.prototype.deleteFile

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 26, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find test files and directory structure
echo "=== Test directory structure ==="
fd -t f -e js -e ts . spec test tests __tests__ 2>/dev/null || echo "No test directories found"

echo -e "\n=== Check for test files in root ==="
fd -t f -e js -e ts | grep -iE '(test|spec)' | head -20

echo -e "\n=== Look at package.json test configuration ==="
cat package.json | jq '.scripts, .devDependencies' 2>/dev/null || cat package.json

echo -e "\n=== Check if there's a test directory ==="
ls -la | grep -iE '(test|spec)'

echo -e "\n=== Find any existing test files ==="
find . -name "*.test.js" -o -name "*.spec.js" -o -name "*test*.js" 2>/dev/null | grep -v node_modules | head -20

Length of output: 797


🏁 Script executed:

#!/bin/bash
# Read the existing test file to understand testing patterns
echo "=== Current test file content ==="
cat spec/test.spec.js

echo -e "\n=== Check if there's a jasmine config ==="
cat spec/support/jasmine.json 2>/dev/null || echo "No jasmine.json found"

echo -e "\n=== Check README for testing instructions ==="
cat README.md | grep -A 10 -i "test" || echo "No test section in README"

Length of output: 2509


@mtrezza Here's a comprehensive integration test suggestion for GCSAdapter.prototype.deleteFile:

// Add to spec/test.spec.js

describe('GCSAdapter.deleteFile integration tests', () => {
  // Only run integration tests if GCS credentials are provided
  if (process.env.GCP_PROJECT_ID && process.env.GCP_KEYFILE_PATH && process.env.GCS_BUCKET) {
    
    let gcsAdapter;
    const testFileName = 'test-delete-file.txt';
    const testFileContent = Buffer.from('test content for deletion');

    beforeEach(() => {
      gcsAdapter = new GCSAdapter({
        projectId: process.env.GCP_PROJECT_ID,
        keyFilename: process.env.GCP_KEYFILE_PATH,
        bucket: process.env.GCS_BUCKET,
        bucketPrefix: 'test-delete-'
      });
    });

    afterEach(async () => {
      // Cleanup: ensure test file is removed even if test fails
      try {
        await gcsAdapter.deleteFile(testFileName);
      } catch (e) {
        // Ignore errors during cleanup
      }
    });

    it('should successfully delete an existing file', async () => {
      // Arrange: Create a file first
      await gcsAdapter.createFile(testFileName, testFileContent);
      
      // Verify file exists before deletion
      const fileExistsBefore = await gcsAdapter.getFileData(testFileName);
      expect(fileExistsBefore).toBeDefined();

      // Act: Delete the file
      const deleteResult = await gcsAdapter.deleteFile(testFileName);

      // Assert: Verify deletion
      expect(deleteResult).toBeDefined(); // v7 returns raw response
      
      // Verify file no longer exists
      try {
        await gcsAdapter.getFileData(testFileName);
        fail('Expected getFileData to throw after file deletion');
      } catch (error) {
        expect(error.code).toBe(404); // GCS returns 404 for missing files
      }
    });

    it('should handle deleting a non-existent file gracefully', async () => {
      // Act & Assert: Try to delete a file that doesn't exist
      const nonExistentFileName = 'non-existent-file-' + Date.now() + '.txt';
      
      try {
        await gcsAdapter.deleteFile(nonExistentFileName);
        // GCS delete is idempotent - deleting non-existent file may succeed
        // This is expected behavior
      } catch (error) {
        // If it throws, verify it's a reasonable error
        expect(error.code).toBeDefined();
        expect([404, 'ENOENT']).toContain(error.code);
      }
    });

    it('should delete file with special characters in name', async () => {
      // Arrange: File with special characters
      const specialFileName = 'test file with spaces & special-chars_123.txt';
      await gcsAdapter.createFile(specialFileName, testFileContent);

      // Act: Delete the file
      const deleteResult = await gcsAdapter.deleteFile(specialFileName);

      // Assert: Verify deletion
      expect(deleteResult).toBeDefined();
      
      try {
        await gcsAdapter.getFileData(specialFileName);
        fail('Expected file to be deleted');
      } catch (error) {
        expect(error.code).toBe(404);
      }
    });

    it('should respect bucketPrefix when deleting files', async () => {
      // This ensures files are deleted from the correct path
      // Arrange: Create file (will be created with prefix)
      await gcsAdapter.createFile(testFileName, testFileContent);

      // Act: Delete should use the same prefix
      await gcsAdapter.deleteFile(testFileName);

      // Assert: File should be gone
      try {
        await gcsAdapter.getFileData(testFileName);
        fail('Expected file to be deleted with correct prefix');
      } catch (error) {
        expect(error.code).toBe(404);
      }
    });

  } else {
    it('skipping integration tests - GCS credentials not provided', () => {
      console.log('Set GCP_PROJECT_ID, GCP_KEYFILE_PATH, and GCS_BUCKET to run integration tests');
      expect(true).toBe(true);
    });
  }
});

Key features of this integration test:

  1. Environment-gated: Only runs when GCS credentials are provided (following existing pattern)
  2. Cleanup hooks: Uses afterEach to ensure test files are cleaned up
  3. Comprehensive coverage:
    • Happy path: create → delete → verify deletion
    • Error handling: deleting non-existent files
    • Special characters in filenames
    • Bucket prefix handling
  4. Async/await: Uses modern Promise syntax (Jasmine 2.4.1 supports this)
  5. Actual GCS interaction: Tests real deletion behavior, not mocked

To run these tests:

export GCP_PROJECT_ID="your-project-id"
export GCP_KEYFILE_PATH="/path/to/keyfile.json"
export GCS_BUCKET="your-test-bucket"
npm test

Alternative: Using GCS Emulator for CI

For automated testing without real GCS credentials, you could also use the Google Cloud Storage Emulator in CI pipelines, which would make these tests run faster and without costs.

Would you like me to also suggest a GitHub Actions workflow that sets up the GCS emulator for automated integration testing?

@mtrezza
Copy link
Copy Markdown
Member

mtrezza commented Feb 26, 2026

@coderabbitai provide a test regarding GCSAdapter.prototype.deleteFile that doesn't requires GCS credentials or parse-server, but ensures the interface contract is honored.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 26, 2026

Tip

For best results, initiate chat on the files or code changes.

@mtrezza Here's a unit test for GCSAdapter.prototype.deleteFile that uses Jasmine spies to mock the GCS SDK and verifies the interface contract:

// Add to spec/test.spec.js

describe('GCSAdapter.deleteFile unit tests (mocked)', () => {
  let gcsAdapter;
  let mockStorage;
  let mockBucket;
  let mockFile;
  let mockDeleteFn;

  beforeEach(() => {
    // Create mock chain: Storage -> bucket() -> file() -> delete()
    mockDeleteFn = jasmine.createSpy('delete');
    
    mockFile = {
      delete: mockDeleteFn
    };
    
    mockBucket = jasmine.createSpyObj('bucket', ['file']);
    mockBucket.file.and.returnValue(mockFile);
    
    mockStorage = jasmine.createSpyObj('Storage', ['bucket']);
    mockStorage.bucket.and.returnValue(mockBucket);
    
    // Create adapter with mocked storage client
    gcsAdapter = new GCSAdapter({
      projectId: 'test-project',
      keyFilename: 'test-key.json',
      bucket: 'test-bucket',
      bucketPrefix: 'test-prefix/'
    });
    
    // Replace the internal GCS client with our mock
    gcsAdapter._gcsClient = mockStorage;
  });

  it('should call GCS delete with correct bucket and file path', (done) => {
    const testFileName = 'myfile.txt';
    
    // Mock successful deletion
    mockDeleteFn.and.callFake((callback) => {
      callback(null, { statusCode: 204 });
    });
    
    gcsAdapter.deleteFile(testFileName)
      .then((result) => {
        // Verify bucket() was called with correct bucket name
        expect(mockStorage.bucket).toHaveBeenCalledWith('test-bucket');
        
        // Verify file() was called with bucketPrefix + filename
        expect(mockBucket.file).toHaveBeenCalledWith('test-prefix/myfile.txt');
        
        // Verify delete() was called
        expect(mockDeleteFn).toHaveBeenCalled();
        
        // Verify result is returned (interface contract: deleteFile returns a promise that resolves with response)
        expect(result).toBeDefined();
        expect(result.statusCode).toBe(204);
        
        done();
      })
      .catch((err) => {
        fail('Promise should not reject: ' + err);
        done();
      });
  });

  it('should reject promise when GCS delete fails', (done) => {
    const testFileName = 'error-file.txt';
    const mockError = new Error('GCS delete failed');
    mockError.code = 403;
    
    // Mock failed deletion
    mockDeleteFn.and.callFake((callback) => {
      callback(mockError, null);
    });
    
    gcsAdapter.deleteFile(testFileName)
      .then(() => {
        fail('Promise should have rejected');
        done();
      })
      .catch((err) => {
        // Verify the error is properly propagated
        expect(err).toBe(mockError);
        expect(err.message).toBe('GCS delete failed');
        expect(err.code).toBe(403);
        
        // Verify methods were still called
        expect(mockStorage.bucket).toHaveBeenCalledWith('test-bucket');
        expect(mockBucket.file).toHaveBeenCalledWith('test-prefix/error-file.txt');
        expect(mockDeleteFn).toHaveBeenCalled();
        
        done();
      });
  });

  it('should handle empty bucketPrefix correctly', (done) => {
    // Create adapter without bucketPrefix
    const adapterNoPrefixgcsAdapter = new GCSAdapter({
      projectId: 'test-project',
      keyFilename: 'test-key.json',
      bucket: 'test-bucket'
      // No bucketPrefix
    });
    adapterNoPrefixgcsAdapter._gcsClient = mockStorage;
    
    const testFileName = 'no-prefix-file.txt';
    
    mockDeleteFn.and.callFake((callback) => {
      callback(null, { statusCode: 204 });
    });
    
    adapterNoPrefixgcsAdapter.deleteFile(testFileName)
      .then(() => {
        // Verify file() was called with just filename (no prefix)
        expect(mockBucket.file).toHaveBeenCalledWith('no-prefix-file.txt');
        done();
      })
      .catch((err) => {
        fail('Promise should not reject: ' + err);
        done();
      });
  });

  it('should return a Promise (interface contract)', () => {
    mockDeleteFn.and.callFake((callback) => {
      callback(null, {});
    });
    
    const result = gcsAdapter.deleteFile('test.txt');
    
    // Verify deleteFile returns a Promise
    expect(result).toEqual(jasmine.any(Promise));
    expect(typeof result.then).toBe('function');
    expect(typeof result.catch).toBe('function');
  });

  it('should handle special characters in filename', (done) => {
    const specialFileName = 'file with spaces & special-chars_123.txt';
    
    mockDeleteFn.and.callFake((callback) => {
      callback(null, {});
    });
    
    gcsAdapter.deleteFile(specialFileName)
      .then(() => {
        // Verify the filename is passed as-is (not encoded or modified)
        expect(mockBucket.file).toHaveBeenCalledWith('test-prefix/file with spaces & special-chars_123.txt');
        done();
      })
      .catch((err) => {
        fail('Promise should not reject: ' + err);
        done();
      });
  });
});

Key features of this unit test:

  1. No external dependencies: Mocks the entire GCS client chain using Jasmine spies
  2. Interface contract verification:
    • ✅ Verifies deleteFile() returns a Promise
    • ✅ Verifies correct bucket name is used
    • ✅ Verifies bucketPrefix + filename path construction
    • ✅ Verifies the delete method is called
    • ✅ Verifies promise resolves on success
    • ✅ Verifies promise rejects with error on failure
  3. Edge cases:
    • Success scenario
    • Error scenario with proper error propagation
    • Empty bucketPrefix handling
    • Special characters in filename
  4. Fast execution: No network calls, runs in milliseconds
  5. Jasmine-compatible: Uses native Jasmine spy framework (no additional dependencies)

This test ensures the adapter correctly implements its contract without requiring real GCS credentials or making actual API calls.

@mtrezza
Copy link
Copy Markdown
Member

mtrezza commented Feb 26, 2026

@Moumouls could you review and add the suggested tests? the upgrade may include breaking changes.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
spec/test.spec.js (1)

40-62: Consider adding semicolons for style consistency.

The file starts with 'use strict' but this new test block omits semicolons on most lines. Consider adding them for consistency with the rest of the codebase's style.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@spec/test.spec.js` around lines 40 - 62, The new test block under
describe('deleteFile') uses inconsistent missing semicolons; update the
beforeEach block and variable declarations (gcsAdapter, mockStorage, mockBucket,
mockFile, mockDelete) to add semicolons at the end of statements to match the
project's `'use strict'` style—ensure each let/assignment and spy setup lines
(e.g., mockDelete = jasmine.createSpy('delete'), mockFile = { delete: mockDelete
}, mockBucket = jasmine.createSpyObj('bucket', ['file']),
mockBucket.file.and.returnValue(mockFile), mockStorage =
jasmine.createSpyObj('storage', ['bucket']),
mockStorage.bucket.and.returnValue(mockBucket), gcsAdapter = new
GCSAdapter(...), gcsAdapter._gcsClient = mockStorage) end with semicolons.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@spec/test.spec.js`:
- Around line 40-62: The new test block under describe('deleteFile') uses
inconsistent missing semicolons; update the beforeEach block and variable
declarations (gcsAdapter, mockStorage, mockBucket, mockFile, mockDelete) to add
semicolons at the end of statements to match the project's `'use strict'`
style—ensure each let/assignment and spy setup lines (e.g., mockDelete =
jasmine.createSpy('delete'), mockFile = { delete: mockDelete }, mockBucket =
jasmine.createSpyObj('bucket', ['file']),
mockBucket.file.and.returnValue(mockFile), mockStorage =
jasmine.createSpyObj('storage', ['bucket']),
mockStorage.bucket.and.returnValue(mockBucket), gcsAdapter = new
GCSAdapter(...), gcsAdapter._gcsClient = mockStorage) end with semicolons.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0744d72 and 4b34545.

📒 Files selected for processing (1)
  • spec/test.spec.js

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 27, 2026
@Moumouls
Copy link
Copy Markdown
Member Author

@coderabbitai does new pushed changes fix concerns ?

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 27, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check recent commits to see what was pushed
echo "=== Recent commits ==="
git log --oneline -5

echo -e "\n=== Latest commit details ==="
git log -1 --stat

echo -e "\n=== Changes in the latest commit ==="
git show --name-status HEAD

echo -e "\n=== Read current test file to see if tests were added ==="
cat spec/test.spec.js

Length of output: 5046


@Moumouls Yes, the newly pushed changes address the main concerns! ✅

What was added

Your latest commit feat: add delete test adds comprehensive unit tests for GCSAdapter.prototype.deleteFile that:

Mock the GCS SDK chain - No credentials required
Verify the Promise interface contract - Checks then() and catch() methods exist
Test successful deletion - Verifies correct bucket, file path (prefix/my-file.txt), and response with statusCode 204
Test error handling - Verifies promise rejection and error propagation (code 403)
Test empty bucketPrefix - Confirms filename is used as-is when prefix is missing

Concerns addressed

1. v7 Breaking Changes: The tests verify that the response object is returned from deleteFile(), which addresses the concern about v7's changed return format. Since you're using the callback-based API (still supported in v7), the adapter should work correctly.

2. Interface Contract: The tests properly verify the method contract without requiring actual GCS credentials or Parse Server, exactly as requested.

Minor note

The only test case from the suggestion that wasn't included is special characters in filenames (e.g., 'file with spaces & special-chars_123.txt'). This is a minor edge case, so it's not critical, but you could add it if you want even more comprehensive coverage.

Overall, excellent work! The tests significantly reduce the risk of the v7 upgrade. 🎉

    /)_/)
   (^.^)
   (> 🧪 Thanks for adding the tests!

Add semicolons in the deleteFile unit test block to match repository style and address review feedback without changing behavior.

Made-with: Cursor
@mtrezza
Copy link
Copy Markdown
Member

mtrezza commented Feb 27, 2026

@Moumouls ready for merge?

@Moumouls
Copy link
Copy Markdown
Member Author

yes @mtrezza ready for merge !

@mtrezza mtrezza merged commit b1446ed into parse-community:main Feb 27, 2026
7 checks passed
parseplatformorg pushed a commit that referenced this pull request Feb 27, 2026
# [2.1.0](2.0.0...2.1.0) (2026-02-27)

### Features

* Upgrade to @google-cloud/storage 7.19.0 ([#57](#57)) ([b1446ed](b1446ed))
@parseplatformorg
Copy link
Copy Markdown

🎉 This change has been released in version 2.1.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants