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

test: Improve Cypress error messages when Linode API errors occur #9777

Conversation

jdamore-linode
Copy link
Contributor

@jdamore-linode jdamore-linode commented Oct 10, 2023

Description 📝

This improves error handling when a Linode API error or validation error occurs during a Cypress test by adding additional information to the command log. This is intended to help troubleshoot test failures.

(Meta: Not sure if this is better suited as test or refactor type, happy to change this to refactor if we think that's more applicable)

Changes 🔄

  • Detect common error types (Linode API error, generic Axios error, Linode schema validation error) and add additional information to the error message when Promise rejects in cy.defer

Preview 📷

Before After
Linode API error:
Linode API Error Before Linode API Error After (2)
Linode schema validation error:
Schema Validation Error Before Schema Validation Error After

How to test 🧪

  • If any of the automated tests fail during a before hook (which has been happening a lot lately), we can rely on the archived screenshots and videos to confirm that this change works.
  • Otherwise, the easiest thing to do is to force a test to make an invalid request and observe the error using yarn cy:debug:
    1. Modify linodeRequest in cypress/support/util/linode-utils.ts (line 10) to contain a short/weak root_pass property
    2. Run yarn cy:debug and select rescue-linode.spec.ts in the UI
    3. Observe that the API responds with a 400 and confirm that detailed messaging is shown in the Cypress UI

As an Author I have considered 🤔

Check all that apply

  • 👀 Doing a self review
  • ❔ Our contribution guidelines
  • 🤏 Splitting feature into small PRs
  • ➕ Adding a changeset
  • 🧪 Providing/Improving test coverage
  • 🔐 Removing all sensitive information from the code and PR description
  • 🚩 Using a feature flag to protect the release
  • 👣 Providing comprehensive reproduction steps
  • 📑 Providing or updating our documentation
  • 🕛 Scheduling a pair reviewing session
  • 📱 Providing mobile support
  • ♿ Providing accessibility support

@jdamore-linode jdamore-linode self-assigned this Oct 10, 2023
@jdamore-linode jdamore-linode requested a review from a team as a code owner October 10, 2023 17:15
@jdamore-linode jdamore-linode requested review from coliu-akamai and tyler-akamai and removed request for a team October 10, 2023 17:15
Comment on lines +18 to +19
// When a Linode APIv4 schema validation error occurs, an array of `APIError`
// objects is thrown rather than a typical `Error` type.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure if this is intended, but I think this is the source of the behavior.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah our error handling/normalization between Cloud Manager and api-v4 is a bit wacky

Are you saying that api-v4 throws Axios errors normally, but APIError[] if it is a schema validation error?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bnussman-akamai Yeah, that's my understanding -- an actual error from the API (like a 400 response, etc.) is an AxiosError<{ errors: APIError[] }> whereas schema validation errors are just APIError[] (in the schema validation Before screenshot in the PR description, Cypress even calls out that it's not an Error that was thrown, but an array).

I don't really think it's a big deal, and even if this is a mistake I'm guessing we're in too deep now, just a little thing that caught me by surprise while working on this!

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, I've noticed the same and agree we're in too deep 😅

On a semi related note, @jcallahan-akamai and I were just recently talking about how we can't see status codes because of our APIError[] pattern. 😣

Copy link
Member

@bnussman-akamai bnussman-akamai left a comment

Choose a reason for hiding this comment

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

Screenshot 2023-10-11 at 3 09 01 PM

Comment on lines +18 to +19
// When a Linode APIv4 schema validation error occurs, an array of `APIError`
// objects is thrown rather than a typical `Error` type.
Copy link
Member

Choose a reason for hiding this comment

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

Yeah our error handling/normalization between Cloud Manager and api-v4 is a bit wacky

Are you saying that api-v4 throws Axios errors normally, but APIError[] if it is a schema validation error?

Copy link
Contributor

@coliu-akamai coliu-akamai left a comment

Choose a reason for hiding this comment

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

Thanks Joe -- error messages were definitely a lot more helpful!

✅ confirmed error message for a weak root_password
✅ confirmed error message for a null root_password

@tyler-akamai
Copy link
Contributor

Confirmed the error message looks good!

Screenshot 2023-10-12 at 11 21 28 AM

Great job ✅

errors: APIError[];
};

/**
Copy link
Contributor

Choose a reason for hiding this comment

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

Very well documented, makes it easy to follow, thank you!

*
* @returns `true` if `e` is a Linode API v4 request error.
*/
const isLinodeApiError = (e: any): e is AxiosError<LinodeApiV4Error> => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Just out of curiosity, do you know why we use LinodeApiV4Error instead of just using APIError[]?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

LinodeApiV4Error is defined towards the top of this file, and it's just an alias for any object with an errors property containing an array of APIErrors.

APIError[] alone isn't applicable here because the error type we're checking for is an Axios error object which contains an HTTP response containing the errors. AxiosError<APIError[]> also isn't applicable since the response body isn't just an array of APIError objects, it's an object itself which contains an errors property.

This could have been expressed as AxiosError<{ errors: APIError[] }>, but I opted to define a type for this within the module because I expected to have to refer to that type a lot (in the end I really didn't).

// and request URL when applicable.
const summary = !!e.response?.status
? `Linode APIv4 request failed with status code ${e.response.status}`
: `Linode APIv4 request failed`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think it would be helpful here to add Linode APIv4 request failed **without status code** for the false condition just so the user knows there wasn't one so they dont spend time looking for it?

const errorDetails = e.response!.data.errors.map((error: APIError) => {
return error.field
? `- ${error.reason} (field '${error.field}')`
: `- ${error.reason}`;
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here.

Its up to you though, I dont know if the status code and the error field are important, if not, feel free to disregard my comments.

const requestInfo =
!!e.request?.responseURL && !!e.config.method
? `\nRequest: ${e.config.method.toUpperCase()} ${e.request.responseURL}`
: '';
Copy link
Contributor

Choose a reason for hiding this comment

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

and here

if (isValidationError(e)) {
// Validation errors do not contain any additional context (request URL, payload, etc.).
// Show the validation error messages instead.
const multipleErrors = e.length > 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Another way to do this:

const errorPlurality = e.length > 1 ? 'errors' : 'error';
const summary = `Request failed with Linode schema validation ${errorPlurality}`;

: 'Request failed with Linode schema validation error';

// Format, accounting for 0, 1, or more errors.
const validationErrorMessage = multipleErrors
Copy link
Contributor

Choose a reason for hiding this comment

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

Since both conditions are pretty similar, another way to do it is:

    const prefix = multipleErrors ? '- ' : '';
    
    // Format, accounting for 0, 1, or more errors.
    const validationErrorMessage = e
        .map((error) =>
            error.field
                ? `${prefix}${error.reason} (field '${error.field}')`
                : `${prefix}${error.reason}`
        )
        .join('\n');

Copy link
Contributor

Choose a reason for hiding this comment

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

or tbh adding a hyphen before any error, even if its just one single error might still help readability. If this is the case, you wouldn't need the conditional.

Up to you though!

@jaalah-akamai jaalah-akamai added the Approved Multiple approvals and ready to merge! label Oct 12, 2023
@jdamore-linode jdamore-linode merged commit cc73646 into linode:develop Oct 16, 2023
11 checks passed
abailly-akamai pushed a commit to abailly-akamai/manager that referenced this pull request Oct 17, 2023
…node#9777)

* Improve error messages when Linode API errors occur

* Added changeset: Improve error message display when Linode API error occurs in Cypress test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Approved Multiple approvals and ready to merge!
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants