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

Creating setuphandler to allow in-game set up to be queried via API #274

Merged
merged 4 commits into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README/TESTING/TESTING_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ A `setup.yaml` file is used to configure custom conditions and fees for the game

### Testing Guidelines

- **General Golang Style Guidelines** Style guidelines can be found within the Golang language specification for the version and question as well as [here](https://google.github.io/styleguide/go/best-practices) for best practices.

- **Pre-deployment Testing:** The goal is to ensure that all tests pass before deploying new software changes. This is particularly important when custom, non-standard conditions are introduced, as these could potentially cause issues that existing tests do not cover. If such conditions arise, it's important to document them so that administrators are aware of potential risks.

- **Infinity Avoidance:** One of the specific test conditions that have been identified is the need to avoid infinity in market calculations. An example of such a test is available [here](https://github.com/openpredictionmarkets/socialpredict/blob/main/backend/tests/market_math/marketvolume_test.go).
Expand Down
22 changes: 22 additions & 0 deletions backend/handlers/setup/setuphandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package setup

import (
"encoding/json"
"net/http"
"socialpredict/setup"
)

func GetSetupHandler(w http.ResponseWriter, r *http.Request) {

appConfig, err := setup.LoadEconomicsConfig()
if err != nil {
http.Error(w, "Failed to load economic config", http.StatusInternalServerError)
return
}

w.Header().Set("Content-Type", "application/json")
err = json.NewEncoder(w).Encode(appConfig.Economics)
if err != nil {
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
}
}
pwdel marked this conversation as resolved.
Show resolved Hide resolved
90 changes: 90 additions & 0 deletions backend/handlers/setup/setuphandler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package setup

import (
"encoding/json"
"net/http"
"net/http/httptest"
"socialpredict/setup"
"strings"
"testing"
)

var loadEconomicsConfig = setup.LoadEconomicsConfig

func TestGetSetupHandler(t *testing.T) {
tests := []struct {
Name string
MockConfigLoader func() (*setup.EconomicConfig, error)
ExpectedStatus int
ExpectedResponse string
IsJSONResponse bool
}{
{
Name: "successful load",
MockConfigLoader: func() (*setup.EconomicConfig, error) {
return setup.LoadEconomicsConfig()
},
Comment on lines +24 to +26
Copy link
Collaborator

Choose a reason for hiding this comment

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

I like the way you did this! Can we add an error case as well, where the config fails to load?

Copy link
Member Author

Choose a reason for hiding this comment

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

ExpectedStatus: http.StatusOK,
ExpectedResponse: `{
"MarketCreation":{"InitialMarketProbability":0.5,"InitialMarketSubsidization":10,"InitialMarketYes":0,"InitialMarketNo":0},
"MarketIncentives":{"CreateMarketCost":10,"TraderBonus":1},
"User":{"InitialAccountBalance":0,"MaximumDebtAllowed":500},
"Betting":{"MinimumBet":1,"BetFees":{"InitialBetFee":1,"EachBetFee":0,"SellSharesFee":0}}}`,
IsJSONResponse: true,
},
}

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
// Replace the actual loader function with the mock
loadEconomicsConfig = test.MockConfigLoader
defer func() { loadEconomicsConfig = setup.LoadEconomicsConfig }()

req, err := http.NewRequest("GET", "/setup", nil)
if err != nil {
t.Fatal(err)
}

rr := httptest.NewRecorder()
handler := http.HandlerFunc(GetSetupHandler)

handler.ServeHTTP(rr, req)

if status := rr.Code; status != test.ExpectedStatus {
t.Errorf("%s: handler returned wrong status code: got %v want %v",
test.Name, status, test.ExpectedStatus)
}

if test.IsJSONResponse {
var expectedjson, actualjson map[string]interface{}
if err := json.Unmarshal([]byte(test.ExpectedResponse), &expectedjson); err != nil {
t.Fatalf("%s: error parsing expected response JSON: %v", test.Name, err)
}
if err := json.Unmarshal(rr.Body.Bytes(), &actualjson); err != nil {
t.Fatalf("%s: error parsing actual response JSON: %v", test.Name, err)
}

if !jsonEqual(expectedjson, actualjson) {
t.Errorf("%s: handler returned unexpected body: got %v want %v",
test.Name, rr.Body.String(), test.ExpectedResponse)
}
} else {
if strings.TrimSpace(rr.Body.String()) != strings.TrimSpace(test.ExpectedResponse) {
t.Errorf("%s: handler returned unexpected body: got %v want %v",
test.Name, rr.Body.String(), test.ExpectedResponse)
}
}
})
}
}

// jsonEqual compares two JSON objects for equality
func jsonEqual(a, b map[string]interface{}) bool {
return jsonString(a) == jsonString(b)
}

// jsonString converts a JSON object to a sorted string
func jsonString(v interface{}) string {
bytes, _ := json.Marshal(v)
return string(bytes)
}
5 changes: 4 additions & 1 deletion backend/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (
adminhandlers "socialpredict/handlers/admin"
betshandlers "socialpredict/handlers/bets"
marketshandlers "socialpredict/handlers/markets"
"socialpredict/handlers/positions"
positions "socialpredict/handlers/positions"
setuphandlers "socialpredict/handlers/setup"
usershandlers "socialpredict/handlers/users"
"socialpredict/middleware"

Expand All @@ -34,6 +35,8 @@ func Start() {
router.HandleFunc("/v0/home", handlers.HomeHandler)
router.HandleFunc("/v0/login", middleware.LoginHandler)

// application setup information
router.HandleFunc("/v0/setup", setuphandlers.GetSetupHandler).Methods("GET")
// markets display, market information
router.HandleFunc("/v0/markets", marketshandlers.ListMarketsHandler).Methods("GET")
router.HandleFunc("/v0/markets/{marketId}", marketshandlers.MarketDetailsHandler).Methods("GET")
Expand Down
Loading