Lesson Details

We have a few code paths that some url paths can run when we receive invalid input. We'll write one new test: handler_handles_empty_pokemon .

The new test, in addition to the one we've already written needs an ApiGatewayProxyRequest which is quite large to write out each time. Inside of the tests sub-module we can add a helper function to construct and return a new ApiGatewayProxyRequest.

The fake_request function will accept a path that we use in the path: Some(path) field of the ApiGatewayProxyRequest.

fn fake_request(
    path: String,
) -> ApiGatewayProxyRequest {
    ApiGatewayProxyRequest {
        resource: None,
        path: Some(path),
        http_method: Method::GET,
        headers: HeaderMap::new(),
        multi_value_headers: HeaderMap::new(),
        query_string_parameters: HashMap::new(),
        multi_value_query_string_parameters:
            HashMap::new(),
        path_parameters: HashMap::new(),
        stage_variables: HashMap::new(),
        request_context:
            ApiGatewayProxyRequestContext {
                account_id: None,
                resource_id: None,
                operation_name: None,
                stage: None,
                domain_name: None,
                domain_prefix: None,
                request_id: None,
                protocol: None,
                identity: ApiGatewayRequestIdentity {
                    cognito_identity_pool_id: None,
                    account_id: None,
                    cognito_identity_id: None,
                    caller: None,
                    api_key: None,
                    api_key_id: None,
                    access_key: None,
                    source_ip: None,
                    cognito_authentication_type: None,
                    cognito_authentication_provider:
                        None,
                    user_arn: None,
                    user_agent: None,
                    user: None,
                },
                resource_path: None,
                authorizer: HashMap::new(),
                http_method: Method::GET,
                request_time: None,
                request_time_epoch: 0,
                apiid: None,
            },
        body: None,
        is_base64_encoded: Some(false),
    }
}

and we can use it in the handler_handles test.

#[tokio::test]
async fn handler_handles() {
    let event = fake_request(
        "/api/pokemon/bulbasaur".to_string(),
    );

    assert_eq!(
        handler(event.clone(), Context::default())
            .await
            .unwrap(),
        ApiGatewayProxyResponse {
            status_code: 200,
            headers: HeaderMap::new(),
            multi_value_headers: HeaderMap::new(),
            body: Some(Body::Text(
                serde_json::to_string(&PokemonHp {
                    name: String::from("Bulbasaur"),
                    hp: 45
                },)
                .unwrap()
            )),
            is_base64_encoded: Some(false),
        }
    )
}

Our new test will panic because of the todo macro, so we can use the should_panic attribute macro to test against the expected panic message.

The test will exercise the 400 codepath, which happens when the path doesn't include a Pokemon name. We could return an empty response as if we hadn't found a Pokemon, but it's most likely that an empty path segment is a client mistake, having passed in an empty string or misconstructed the path.

#[tokio::test]
#[should_panic(expected = "not yet implemented")]
async fn handler_handles_empty_pokemon() {
    let event =
        fake_request("/api/pokemon//".to_string());
    handler(event.clone(), Context::default())
        .await
        .unwrap();
}

All tests should pass at this point