Search code examples
typescriptamazon-web-servicesamazon-s3ts-jest

Is there's a way to spyOn for aws S3 bucket to test funcionality?


Working with typescript project we are using s3 to save images.

The s3 operations aren't called directly from s3 but a service called FileService that has the basic structure to handle all the basic operations looks like this:

static getSignedUrlFromS3(info: SignedUrlInfo) {
    try {
      return s3.getSignedUrlPromise(info.operationType, {
        Bucket: info.bucket,
        Key: info.key,
        Expires: info.expires,
      });
    } catch (error) {
      winston.error(
        `Error retrieving signed url '${info.key}' from S3 Bucket '${info.bucket}'`,
        error,
      );

      return Promise.resolve();
    }
  }

  /**
   * Get a list of objects from a S3 bucket.
   */
  static getBucketObjectsList(info: ObjectListInfo) {
    try {
      return s3
        .listObjectsV2({
          Bucket: info.bucket,
          Delimiter: info.delimiter,
          Prefix: info.prefix,
        })
        .promise();
    } catch (error) {
      winston.error(
        `Error retrieving object list from S3 Bucket '${info.bucket}'`,
        error,
      );

      return Promise.resolve();
    }

  /**
   * Creates a S3 Presigned Post that for add an object into S3 bucket
   */
  static createS3PresignedPost(info: PresignedUrlInfo) {
    try {
      const params = {
        Bucket: info.bucket,
        Fields: {
          key: info.key,
        },
        Expires: info.expires,
      };

      return s3.createPresignedPost(params);
    } catch (error) {
      winston.error(
        `Error creating presigned POST for S3 Bucket '${info.bucket}'`,
        error,
      );

      return;
    }
  }

So basically I have another Service that consumes this one calling the specific methods of the other service as follow:

static getOne(key: string): Promise<Response> {
    return new Promise(async (resolve, reject) => {
      const signedUrl = await FileService.getSignedUrlFromS3({
        bucket: bucketName,
        operationType: 'getObject',
        key: key,
        expires: 60 * 60 * 24,
      });
      if (typeof signedUrl === 'string') {
        resolve({
          key,
          url: signedUrl,
          date: this._getDateFromKey(key),
        });
      } else {
        winston.error(`Error finding ${bucketName}/${key} in S3`);
        reject(new ServerExceptionBadRequest('Unable to get media file.'));
      }
    });
  }

So my question is how can I mock S3 to simulate the functionality of the services like make a signedPost GetObject, CopyObject etc.

What I have seen in documentation about is something like this:

jest.mock('@aws-sdk/client-s3');

import { VehicleMediaService } from '@api-services/VehicleMediaService';
import {
  MediaStatus,
  MediaType,
} from '@api-services/VehicleMediaService.interfaces';

import * as S3 from '@aws-sdk/client-s3/S3.spec';

const mocked = S3 as jest.Mocked<typeof S3>;

But not quite super sure about how to used. Thank you very much for your help.


Solution

  • The solution is mock the services using the aws-sdk. For example:

    jest.spyOn(yourService, 'yourMethod').mockImplementation(() => {
          const response = new Response<S3.Types.ListObjectsV2Output, AWSError>();
           
          const result: PromiseResult<S3.Types.ListObjectsV2Output, AWSError> = {
            $response: response,
            Contents: objectFiles,
          };
          return Promise.resolve(result);
        });
    

    So that way you can mock any aws service.