Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

import { util } from '@aws-appsync/utils' is empty object #363

Open
naedx opened this issue Jul 25, 2024 · 4 comments
Open

import { util } from '@aws-appsync/utils' is empty object #363

naedx opened this issue Jul 25, 2024 · 4 comments

Comments

@naedx
Copy link

naedx commented Jul 25, 2024

I'm having some trouble using the @aws-appsync/utils (v1.8.0) in my Vitest tests. Given the following test script util is equal to {};

import { util } from '@aws-appsync/utils';
import { describe, expect, test } from 'vitest';

describe('Use AppSync @aws-appsync/utils', () => {

  test('util should not be empty', () => {
    console.log(util); // prints {}
    expect(util).not.toMatchObject({});
  });

  test('util.autoId() should be a function', () => {

    expect(util?.autoId).toBeTypeOf('function');

    const id = util.autoId();

    expect(id).toBeDefined();
  });

  test('util.time.nowISO8601() should produce a date', () => {

    expect(util.time.nowISO8601).toBeTypeOf('function');

    const t = util.time.nowISO8601();

    console.log(`Generated time: ${t}`);

    expect(t).toBeDefined();
  });

});

Importing Context from @aws-appsync/utils works as expected.

I've placed a full, self contained example here.

@naedx
Copy link
Author

naedx commented Jul 26, 2024

Stumbled upon this on r/aws by u/seedsseedsseeds also:

My team has been using AppSync for a couple years and for the most part, it's quite nice. Especially with JS resolvers (a pox upon VTL!) and the recently introduced AWS::Serverless::GraphqlApi type for AWS SAM, the developer experience has been greatly improved.

However, I've recently been working on improving our test suite, and have run aground upon a baffling obstacle -- @aws-appsync/utils doesn't allow for local testing!

Because the JS resolvers have a limited runtime, we're using util.nowEpochSeconds() to generate timestamps and util.dynamodb.toMapValues to marshall the dynamo commands, and the npm-installed version of the @aws-appsync/utils library has no implementation for these, only types.

I know AWS offers ways to test resolver definitions against the services itself, but these are simple resolvers, I just want a simple unit test harness to make sure the right values are defined.

Am I crazy, or missing something? Does anyone have a solution for how to test these things locally? Do I just have to waste the time to implement them myself and mock them?

They had to mock the library themselves. What is the correct solution?

@nat-jones
Copy link

I am having this issue trying to import { util } from '@aws-appsync/utils'; in a lambda function. Any update?

@onlybakam
Copy link
Contributor

Hi.
Javascript resolvers run on a runtime within the AppSync service. You can call AppSync's EvaluateCode API to test your resolver code. The @aws-appsync/utils package contains type definitions to help you implement your code.

See details here: https://docs.aws.amazon.com/appsync/latest/devguide/test-resolvers.html

@naedx
Copy link
Author

naedx commented Aug 12, 2024

Hi @onlybakam , thanks for your reply. Does that mean that the best solution is to (1) mock the functions if you need to test locally and (2) use EvaluateCode otherwise?

For anyone stumbling on this, you can mock the required functions in Vitest using:

// mock the required functioins from @aws-appsync/utils
vi.mock('@aws-appsync/utils', () => {

  const originalAppSyncUtils = require('@aws-appsync/utils');
  const { marshall } = require('@aws-sdk/util-dynamodb');
  const { v4 } = require('uuid');

  return {
    ...originalAppSyncUtils,
    util: {
      error: (msg: string | undefined) => { throw new Error(msg) },
      autoId: () => v4(),
      dynamodb: {
        toMapValues: (val: any) => { return marshall(val); }
      },
      time: {
        nowEpochSeconds: () => Math.floor(Date.now() / 1000),
        nowISO8601: () => new Date().toISOString()
      }
    }
  }
});

and you can use EvaluateCode approach like so:

//...

describe('Online/AWS Runtime Resolver Test', async () => {
  const client = new AppSync({ region: 'us-east-1' });
  const runtime = { name: 'APPSYNC_JS', runtimeVersion: '1.0.0' };
  const __dirname = path.resolve('amplify/data');

  test('Create todo resolver', async () => {
    const createTodoInput = {
      "content": `Test subject ${uuidV4()}`
    };

    const code = fs.readFileSync(__dirname + '/features/todo/todo.create.resolver.js', 'utf8')

    const contextJSON = createTestContextForEvaluate<{ input: typeof createTodoInput }>({ input: createTodoInput });

    const response = await client.evaluateCode({
      code,
      context: JSON.stringify(contextJSON),
      runtime,
      function: 'request'
    }).promise();

    const result = JSON.parse(response.evaluationResult!);

    expect(result?.key?.id?.S).toBeTypeOf('string');
    expect(result?.attributeValues?.content?.S).toEqual(contextJSON.arguments.input.content);
  });
});

//...

I've put a full example here https://github.com/naedx/amplify-playground/tree/dev/projects/amplify-appsync-vitest

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants