NewTestEndpoint() function must only send the HTTP request to the
server and return the code, response and an error. This serves cases
where the response cannot be compared with a predefined message e.g.
when a new user is added we should not assume its assigned ID
beforehand. The assertions of probable error, non-200 codes and non
expected response bodies should be done inside the test function.
The problem started with an internal error in the database (HTTP
code 500, pq: duplicate key value) while trying to POST at /api/user
with body {"user":{newUserObject}} to the backend, that already had
three users User0 (Admin), UserA and UserB. Every test was
succeeding.
Eventually, the source of the problem was the initialization of the
field `ID` (primary key) of the embedded struct `Model` in model
`User`. When the `User` global variables User0, UserA and UserB
were created in `common/testdata.go` the `Model.ID` field was set
manually to the values 1, 2 and 3 respectively. This ofc was
unnecessary but those variables were used for comparison with the
relevant responses.
When gorm (or postgres, not entirely sure) have a new variable of a
model type, it will check if its primary key is zero (which means is
uninitialized) and will set it to the last used value +1. That means
that the new user that we were trying to insert in the DB got the
primary key value 1. This value was already in use by the User0 so
the DB refused to add the new user since there was a duplicate
primary key value. That's why we were getting an postgre's error and
HTTP 500.
Hence to fix the bug this commit:
- Removes `User.ID` initialization from `common/testdata.go`
- Omits the `User.ID` from the json marshaler by setting the struct
json tag to `json:"-"`. This is done because the request body is
compared to the response *after* is serialized by a call to
json.Marshal(). Since the `User.ID` is always uninitialized (value
0) the jsondiff.Compare() will always return a "NoMatch".
- Includes check for return of "SupersetMatch" in
NewTestEndpoint()'s jsondiff.Compare() call. Since the `User.ID` is
not serialized the json representation of the request will not
include an "id" key. On the contrary, the response *has* an "id" key
so it will always going to be a superset json object of the
serialized request.
- Modifies the validator's data struct to match the form of the
request ({"modelKey":{requestBody}}) by adding an intermediate
struct with the proper tag `json:"user"`.
As the expectedResponse variable the responseBody is now marshaled
in the body of the function and not in the test file. This
simplifies the body of the testing function, improves the semantic,
avoids marshaling repetition and checks for marshaling errors.
- As NewTestEndpoint() the credentials are passed as interface{} and
marsaling is happening inside the function.
- Add check for ignored error from http.NewRequest()
Problems arise from the fact that the are two kind of successful
responses: The array of objects response like
{"key":[{obj1},{obj2},...]} and the single object response like
{"key":{obj}}. The function will try to check if the response can be
unmarshaled in an array of generic type variables so to match the
first case. If not it will try to unmarshal to a single generic type
variable and 1 will be returned as the length of the response. If
this will also fail -1 will be returned.
- The function must return the token and an error (string, error).
In case of an error the token is going to be an empty string. The
assertion for the error __must__ be executed in the body of the
test. The utility functions __must not__ panic. All of the tests
must use this function in the future.
- NewTestEndpoint() should replace TestEndpoint() since it is
returning an error in case that the code or the response is not
matching the expected one. The assertion __must__ be executed in the
body of the actual test (of the corresponding package) so the
printed error message can include the right number of line and the
file where the assertion failed.
- The function is used now only in package scenario tests.
- Typedefs the name of a model (ModelName) and the CRUD operations
(CRUD).
- Defines constants for models's names and the 4 CRUD operations
- Changes the corresponding functions that need to validate the role