Skip to content

Commit

Permalink
feat: add utg guide in mux-sql (#107)
Browse files Browse the repository at this point in the history
Signed-off-by: Animesh Pathak <[email protected]>
  • Loading branch information
Sonichigo authored Jun 14, 2024
1 parent 1f31169 commit f534e63
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 5 deletions.
55 changes: 50 additions & 5 deletions mux-sql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ curl --silent -O -L https://keploy.io/install.sh && source install.sh
Using the docker-compose file we will start our postgres instance:-

```bash
# Start Postgres
docker-compose up -d postgres
```

Expand Down Expand Up @@ -48,7 +47,7 @@ Make API Calls using Hoppscotch, Postman or cURL command. Keploy with capture th

To genereate testcases we just need to make some API calls. You can use [Postman](https://www.postman.com/), [Hoppscotch](https://hoppscotch.io/), or simply `curl`

### 1. Generate shortned url
1. Generate shortned url

```bash
curl --request POST \
Expand All @@ -68,7 +67,7 @@ this will return the response.
}
```

### 2. Fetch the Products
2. Fetch the Products
```bash
curl --request GET \
--url http://localhost:8010/products
Expand All @@ -80,7 +79,7 @@ we will get output:
[{"id":1,"name":"Bubbles","price":123},{"id":2,"name":"Bubbles","price":123}]
```

### 3. Fetch a single product
3. Fetch a single product

```sh
curl --request GET \
Expand All @@ -97,6 +96,8 @@ Now, since these API calls were captured as editable testcases and written to ``

Now let's run the test mode (in the mux-sql directory, not the Keploy directory).
### Run captured testcases
```shell
sudo -E keploy test -c "./test-app-product-catelog" --delay 10 --goCoverage
```
Expand All @@ -109,4 +110,48 @@ So no need to setup fake database/apis like Postgres or write mocks for them. Ke

With the three API calls that we made, we got around `55.6%` of coverage

![test coverage](./img/coverage.png?raw=true)
![test coverage](./img/coverage.png?raw=true)

## Create Unit Testcase with Keploy

### Prequiste
AI model API_KEY to use:

- OpenAI's GPT-4o.
- Alternative LLMs via [litellm](https://github.com/BerriAI/litellm?tab=readme-ov-file#quick-start-proxy---cli).
### Setup
Download Cobertura formatted coverage report dependencies.
```bash
go install github.com/axw/gocov/[email protected]
go install github.com/AlekSi/[email protected]
```
Get API key from [OpenAI](https://platform.openai.com/) or API Key from other LLM
```bash
export API_KEY=<LLM_MODEL_API_KEY>
```
### Generate Unit tests
Let's check the current code coverage of out application : -

```bash
go test -v ./... -coverprofile=coverage.out && gocov convert coverage.out | gocov-xml > coverage.xml
```
We got around 44.1% of code coverage.

![Go Test](./img/mux-utg.png?raw=true)

Now, let's run create keploy to create testcases.
```bash
keploy gen --sourceFilePath="app.go" --testFilePath="app_test.go" --testCommand="go test -v ./... -coverprofile=coverage.out && gocov convert coverage.out | gocov-xml > coverage.xml" --coverageReportPath="./coverage.xml" --expectedCoverage 70 --maxIterations 2
```
With the above command, Keploy will generate new testcases in the our `app_test.go` can increase code coverage upto 70%.
![Keploy Mux UTG](./img/mux-utg-codecov.png?raw=true)
113 changes: 113 additions & 0 deletions mux-sql/app_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package main_test

import (
"bytes"
"encoding/json"
"log"
"net/http"
"net/http/httptest"
"os"
"testing"

Check failure on line 10 in mux-sql/app_test.go

View workflow job for this annotation

GitHub Actions / lint (mux-sql)

File is not `gofmt`-ed with `-s` (gofmt)
"test-app-product-catelog"
)

var a main.App

func TestMain(m *testing.M) {
a.Initialize(

Check failure on line 17 in mux-sql/app_test.go

View workflow job for this annotation

GitHub Actions / lint (mux-sql)

Error return value of `a.Initialize` is not checked (errcheck)
"localhost", "postgres", "password",
"postgres")
ensureTableExists()
code := m.Run()
clearTable()
os.Exit(code)
}

func ensureTableExists() {
if _, err := a.DB.Exec(tableCreationQuery); err != nil {
log.Fatal(err)
}
}

func clearTable() {
a.DB.Exec("DELETE FROM products")

Check failure on line 33 in mux-sql/app_test.go

View workflow job for this annotation

GitHub Actions / lint (mux-sql)

Error return value of `a.DB.Exec` is not checked (errcheck)
a.DB.Exec("ALTER SEQUENCE products_id_seq RESTART WITH 1")

Check failure on line 34 in mux-sql/app_test.go

View workflow job for this annotation

GitHub Actions / lint (mux-sql)

Error return value of `a.DB.Exec` is not checked (errcheck)
}

const tableCreationQuery = `CREATE TABLE IF NOT EXISTS products
(
id SERIAL,
name TEXT NOT NULL,
price NUMERIC(10,2) NOT NULL DEFAULT 0.00,
CONSTRAINT products_pkey PRIMARY KEY (id)
)`

func TestEmptyTable(t *testing.T) {
clearTable()

req, _ := http.NewRequest("GET", "/products", nil)
response := executeRequest(req)

checkResponseCode(t, http.StatusOK, response.Code)

if body := response.Body.String(); body != "[]" {
t.Errorf("Expected an empty array. Got %s", body)
}
}

func executeRequest(req *http.Request) *httptest.ResponseRecorder {
rr := httptest.NewRecorder()
a.Router.ServeHTTP(rr, req)

return rr
}

func checkResponseCode(t *testing.T, expected, actual int) {
if expected != actual {
t.Errorf("Expected response code %d. Got %d\n", expected, actual)
}
}

func TestGetNonExistentProduct(t *testing.T) {
clearTable()

req, _ := http.NewRequest("GET", "/product/11", nil)
response := executeRequest(req)

checkResponseCode(t, http.StatusNotFound, response.Code)

var m map[string]string
json.Unmarshal(response.Body.Bytes(), &m)

Check failure on line 80 in mux-sql/app_test.go

View workflow job for this annotation

GitHub Actions / lint (mux-sql)

Error return value of `json.Unmarshal` is not checked (errcheck)
if m["error"] != "Product not found" {
t.Errorf("Expected the 'error' key of the response to be set to 'Product not found'. Got '%s'", m["error"])
}
}

func TestCreateProduct(t *testing.T) {

clearTable()

var jsonStr = []byte(`{"name":"test product", "price": 11.22}`)
req, _ := http.NewRequest("POST", "/product", bytes.NewBuffer(jsonStr))
req.Header.Set("Content-Type", "application/json")

response := executeRequest(req)
checkResponseCode(t, http.StatusCreated, response.Code)

var m map[string]interface{}
json.Unmarshal(response.Body.Bytes(), &m)

Check failure on line 98 in mux-sql/app_test.go

View workflow job for this annotation

GitHub Actions / lint (mux-sql)

Error return value of `json.Unmarshal` is not checked (errcheck)

if m["name"] != "test product" {
t.Errorf("Expected product name to be 'test product'. Got '%v'", m["name"])
}

if m["price"] != 11.22 {
t.Errorf("Expected product price to be '11.22'. Got '%v'", m["price"])
}

// the id is compared to 1.0 because JSON unmarshaling converts numbers to
// floats, when the target is a map[string]interface{}
if m["id"] != 1.0 {
t.Errorf("Expected product ID to be '1'. Got '%v'", m["id"])
}
}
Binary file added mux-sql/img/mux-utg-codecov.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added mux-sql/img/mux-utg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit f534e63

Please sign in to comment.