Skip to content

Commit 60db0c1

Browse files
committed
feat: add venom tests with smocker
1 parent 66ef543 commit 60db0c1

File tree

10 files changed

+200
-19
lines changed

10 files changed

+200
-19
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ test:
3737
.PHONY: test-integration
3838
test-integration: $(VENOM)
3939
. .env; \
40-
$(VENOM) run venom/**/*.venom.yml --var="myapp=$${MY_APP}" --var="pgsql_dsn=$${POSTGRES_DSN}" --format=xml --output-dir=./dist
40+
$(VENOM) run venom/**/*.venom.yml --var="myapp=$${MY_APP}" --var="mock_server=$${MOCK_SERVER_ADMIN}" --var="pgsql_dsn=$${POSTGRES_DSN}" --format=xml --output-dir=./dist
4141

4242
.PHONY: integration
4343
PID_FILE:=/tmp/example.test.pid
4444
integration: build $(VENOM)
4545
. .env; \
4646
./dist/example.test -test.coverprofile=./dist/example.venom.cover.out > ./dist/example.app.log 2>&1 & echo $$! > $(PID_FILE); \
4747
sleep 5; \
48-
$(VENOM) run venom/**/*.venom.yml --var="myapp=$${MY_APP}" --var="pgsql_dsn=$${POSTGRES_DSN}" --format=xml --output-dir=./dist | tee ./dist/example.venom.log 2>&1 || res=$$?; \
48+
$(VENOM) run venom/**/*.venom.yml --var="myapp=$${MY_APP}" --var="mock_server=$${MOCK_SERVER_ADMIN}" --var="pgsql_dsn=$${POSTGRES_DSN}" --format=xml --output-dir=./dist | tee ./dist/example.venom.log 2>&1 || res=$$?; \
4949
kill `cat $(PID_FILE)` 2> /dev/null || true; \
5050
go tool cover -html=./dist/example.venom.cover.out -o ./dist/example.venom.cover.html; \
5151
exit $$res

sdks/hotels.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ func (c *hotelsClient) GetHotels(ctx context.Context) ([]types.Hotel, error) {
3232
return hotels, err
3333
}
3434
defer resp.Body.Close()
35+
if err = errorHook(resp); err != nil {
36+
return hotels, err
37+
}
3538
err = json.NewDecoder(resp.Body).Decode(&hotels)
3639
return hotels, err
3740
}
@@ -47,6 +50,9 @@ func (c *hotelsClient) GetHotelByName(ctx context.Context, name string) (types.H
4750
return hotel, err
4851
}
4952
defer resp.Body.Close()
53+
if err = errorHook(resp); err != nil {
54+
return hotel, err
55+
}
5056
err = json.NewDecoder(resp.Body).Decode(&hotel)
5157
return hotel, err
5258
}

sdks/sdks.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package sdks
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
)
8+
9+
type SDKError struct {
10+
Code int `json:"code"`
11+
Message string `json:"message"`
12+
}
13+
14+
func (m *SDKError) Error() string {
15+
return fmt.Sprintf("request failed with code %d: %s", m.Code, m.Message)
16+
}
17+
18+
func errorHook(resp *http.Response) error {
19+
if resp.StatusCode < 400 {
20+
return nil
21+
}
22+
var errSDK SDKError
23+
err := json.NewDecoder(resp.Body).Decode(&errSDK)
24+
if err != nil {
25+
return &SDKError{Code: resp.StatusCode, Message: err.Error()}
26+
}
27+
errSDK.Code = resp.StatusCode
28+
return &errSDK
29+
}

sdks/users.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ func (c *usersClient) GetUsers(ctx context.Context) ([]types.User, error) {
3232
return users, err
3333
}
3434
defer resp.Body.Close()
35+
if err = errorHook(resp); err != nil {
36+
return users, err
37+
}
3538
err = json.NewDecoder(resp.Body).Decode(&users)
3639
return users, err
3740
}
@@ -47,6 +50,9 @@ func (c *usersClient) GetUserByName(ctx context.Context, name string) (types.Use
4750
return user, err
4851
}
4952
defer resp.Body.Close()
53+
if err = errorHook(resp); err != nil {
54+
return user, err
55+
}
5056
err = json.NewDecoder(resp.Body).Decode(&user)
5157
return user, err
5258
}

server/server.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ func Serve(config types.Config) {
2222

2323
engine := echo.New()
2424
engine.Use(middleware.Recover(), middleware.LoggerWithConfig(middleware.LoggerConfig{
25-
Format: "method=${method}, uri=${uri}, status=${status}\n",
25+
Format: "HTTP Request time=${time_rfc3339}, method=${method}, uri=${uri}, status=${status}\n",
2626
}))
27+
2728
reservations := engine.Group("/reservations")
2829
{
2930
ctrl := ctrls.Reservations

server/services/services.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,15 @@ func (s *reservations) CreateReservation(ctx context.Context, reservation types.
6161
var res types.Reservation
6262
user, err := s.users.GetUserByName(ctx, reservation.User)
6363
if err != nil {
64-
return res, err
64+
return res, fmt.Errorf("unable to retrieve user %q: %w", reservation.User, err)
6565
}
6666
hotel, err := s.hotels.GetHotelByName(ctx, reservation.Hotel)
6767
if err != nil {
68-
return res, err
68+
return res, fmt.Errorf("unable to retrieve hotel %q: %w", reservation.Hotel, err)
6969
}
7070
reservations, err := s.db.SelectReservationsByHotel(ctx, hotel.ID)
7171
if err != nil {
72-
return res, err
72+
return res, fmt.Errorf("unable to retrieve reservations from database: %w", err)
7373
}
7474

7575
reservedRooms := int64(0)
@@ -81,9 +81,13 @@ func (s *reservations) CreateReservation(ctx context.Context, reservation types.
8181
return res, fmt.Errorf("can't create reservation: %w", ErrInsufficientCapacity)
8282
}
8383

84-
return s.db.InsertReservation(ctx, types.Reservation{
84+
result, err := s.db.InsertReservation(ctx, types.Reservation{
8585
UserID: user.ID,
8686
HotelID: hotel.ID,
8787
RoomNumber: reservation.Rooms,
8888
})
89+
if err != nil {
90+
return res, fmt.Errorf("unable to create reservation on database: %w", err)
91+
}
92+
return result, nil
8993
}

venom/assets/fixtures/reservations.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@
99
- id: 3
1010
user_id: 1
1111
hotel_id: 3
12+
room_number: 2
13+
- id: 4
14+
user_id: 1
15+
hotel_id: 1
1216
room_number: 2
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# UC 1
2+
- request:
3+
path: /users/user1
4+
method: GET
5+
response:
6+
status: 404
7+
headers:
8+
Content-Type: application/json
9+
body: >
10+
{
11+
"message": "not found"
12+
}
13+
14+
# UC 2
15+
- request:
16+
path: /users/user2
17+
method: GET
18+
response:
19+
headers:
20+
Content-Type: application/json
21+
body: >
22+
{
23+
"id": 2,
24+
"name": "user2"
25+
}
26+
- request:
27+
path: /hotels/hotel2
28+
method: GET
29+
response:
30+
status: 404
31+
headers:
32+
Content-Type: application/json
33+
body: >
34+
{
35+
"message": "not found"
36+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
- request:
2+
path: /users/user1
3+
method: GET
4+
response:
5+
headers:
6+
Content-Type: application/json
7+
body: >
8+
{
9+
"id": 1,
10+
"name": "user1"
11+
}
12+
- request:
13+
path: /hotels/hotel1
14+
method: GET
15+
response:
16+
headers:
17+
Content-Type: application/json
18+
body: >
19+
{
20+
"id": 1,
21+
"name": "hotel1",
22+
"rooms": 10
23+
}

venom/reservations.venom.yml

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ testcases:
1111
assertions:
1212
- result.err ShouldBeEmpty
1313

14-
- name: Retrieve reservations
14+
- name: Retrieve existings reservations
1515
steps:
1616
- type: http
1717
method: GET
@@ -21,7 +21,7 @@ testcases:
2121
- result.bodyjson.__type__ ShouldEqual Array
2222
- result.bodyjson.__len__ ShouldEqual 3
2323

24-
- name: Retrieve one existing reservation
24+
- name: Retrieve an existing reservation
2525
steps:
2626
- type: http
2727
method: GET
@@ -33,23 +33,95 @@ testcases:
3333
- result.bodyjson.hotel_id ShouldEqual 2
3434
- result.bodyjson.room_number ShouldEqual 3
3535

36-
- name: Try to retrieve unexisting reservation
36+
- name: Try to retrieve an unexisting reservation
3737
steps:
3838
- type: http
3939
method: GET
40-
url: "{{.myapp}}/reservations/4"
40+
url: "{{.myapp}}/reservations/5"
4141
assertions:
4242
- result.statuscode ShouldEqual 404
43-
- "result.bodyjson.message ShouldEqual unable to retrieve reservation for id '4': not found"
43+
- "result.bodyjson.message ShouldEqual unable to retrieve reservation for id '5': not found"
4444

45-
- name: Get one reservation
45+
- name: Create a reservation with missing data
4646
steps:
4747
- type: http
48-
method: GET
49-
url: "{{.myapp}}/reservations/1"
48+
method: POST
49+
url: "{{.mock_server}}/mocks?session=create_reservation_missing_data"
50+
bodyFile: ./assets/mocks/create_reservation_not_found.mocks.yml
5051
assertions:
5152
- result.statuscode ShouldEqual 200
52-
- result.bodyjson.id ShouldEqual 1
53-
- result.bodyjson.user_id ShouldEqual 2
54-
- result.bodyjson.hotel_id ShouldEqual 2
55-
- result.bodyjson.room_number ShouldEqual 3
53+
- type: http
54+
method: POST
55+
url: "{{.myapp}}/reservations"
56+
headers:
57+
Content-Type: application/json
58+
body: |
59+
{
60+
"hotel": "hotel1",
61+
"user": "user1",
62+
"rooms": 2
63+
}
64+
assertions:
65+
- result.statuscode ShouldEqual 500
66+
- 'result.bodyjson.message ShouldEqual unable to retrieve user "user1": request failed with code 404: not found'
67+
- type: http
68+
method: POST
69+
url: "{{.myapp}}/reservations"
70+
headers:
71+
Content-Type: application/json
72+
body: |
73+
{
74+
"hotel": "hotel2",
75+
"user": "user2",
76+
"rooms": 2
77+
}
78+
assertions:
79+
- result.statuscode ShouldEqual 500
80+
- 'result.bodyjson.message ShouldEqual unable to retrieve hotel "hotel2": request failed with code 404: not found'
81+
82+
- name: Create a reservation not enough rooms
83+
steps:
84+
- type: http
85+
method: POST
86+
url: "{{.mock_server}}/mocks?session=create_reservation_not_enough_rooms"
87+
bodyFile: ./assets/mocks/create_reservation_ok.mocks.yml
88+
assertions:
89+
- result.statuscode ShouldEqual 200
90+
- type: http
91+
method: POST
92+
url: "{{.myapp}}/reservations"
93+
headers:
94+
Content-Type: application/json
95+
body: |
96+
{
97+
"hotel": "hotel1",
98+
"user": "user1",
99+
"rooms": 20
100+
}
101+
assertions:
102+
- result.statuscode ShouldEqual 409
103+
- "result.bodyjson.message ShouldEqual can't create reservation: not enough room available"
104+
105+
- name: Create a reservation ok
106+
steps:
107+
- type: http
108+
method: POST
109+
url: "{{.mock_server}}/mocks?session=create_reservation_ok"
110+
bodyFile: ./assets/mocks/create_reservation_ok.mocks.yml
111+
assertions:
112+
- result.statuscode ShouldEqual 200
113+
- type: http
114+
method: POST
115+
url: "{{.myapp}}/reservations"
116+
headers:
117+
Content-Type: application/json
118+
body: |
119+
{
120+
"hotel": "hotel1",
121+
"user": "user1",
122+
"rooms": 2
123+
}
124+
assertions:
125+
- result.statuscode ShouldEqual 200
126+
- result.bodyjson.id ShouldNotBeEmpty
127+

0 commit comments

Comments
 (0)