From 7a464c2f0952ed3e22cf73bc8568bdb273053df1 Mon Sep 17 00:00:00 2001 From: Bel LaPointe Date: Fri, 7 Apr 2023 13:44:42 -0600 Subject: [PATCH] test http POST api/questions/QID/answers --- http.go | 34 +++++++++++++++++++++++++++++++++- http_test.go | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 http_test.go diff --git a/http.go b/http.go index 845d850..6f9ee0a 100644 --- a/http.go +++ b/http.go @@ -7,7 +7,10 @@ import ( "fmt" "net/http" "os" + "os/signal" + "path" "strings" + "syscall" "time" ) @@ -17,6 +20,9 @@ type Context struct { } func HTTP(port int, db DB) error { + ctx, can := signal.NotifyContext(context.Background(), syscall.SIGINT) + defer can() + foo := func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/" { httpGUI(w, r) @@ -28,7 +34,12 @@ func HTTP(port int, db DB) error { } foo = withAuth(foo) foo = withDB(foo, db) - return http.ListenAndServe(fmt.Sprintf(":%d", port), http.HandlerFunc(foo)) + foo = withCtx(foo, ctx) + go func() { + http.ListenAndServe(fmt.Sprintf(":%d", port), http.HandlerFunc(foo)) + }() + <-ctx.Done() + return nil } func extract(ctx context.Context) Context { @@ -41,6 +52,13 @@ func inject(ctx context.Context, v Context) context.Context { return context.WithValue(ctx, "__context", v) } +func withCtx(foo http.HandlerFunc, ctx context.Context) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + r = r.WithContext(ctx) + foo(w, r) + } +} + func withDB(foo http.HandlerFunc, db DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { c := extract(r.Context()) @@ -100,4 +118,18 @@ func httpAssignments(ctx context.Context) (interface{}, error) { } func httpPostQuestionAnswers(w http.ResponseWriter, r *http.Request) { + idq := IDQ(path.Base(strings.Split(r.URL.Path, "/answers")[0])) + var payload struct { + Answer string `json:"answer"` + Passed bool `json:"passed"` + } + if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + ctx := extract(r.Context()) + if err := ctx.DB.PushAnswer(ctx.User, idq, Renderable(payload.Answer), payload.Passed); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } } diff --git a/http_test.go b/http_test.go new file mode 100644 index 0000000..271741c --- /dev/null +++ b/http_test.go @@ -0,0 +1,40 @@ +package main + +import ( + "net/http" + "net/http/httptest" + "os" + "path" + "strings" + "testing" +) + +func TestHTTPPostQuestionAnswers(t *testing.T) { + p := path.Join(t.TempDir(), "db.yaml") + os.WriteFile(p, []byte("{}"), os.ModePerm) + db, err := newYamlDB(p) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + r := httptest.NewRequest( + http.MethodPost, + "/api/questions/QID/answers", + strings.NewReader(`{"answer":"a", "passed": false}`), + ) + r.SetBasicAuth("u", "") + + withAuth(withDB(httpPostQuestionAnswers, db))(w, r) + + if w.Code != http.StatusOK { + t.Error(w.Code) + } + t.Logf("response: %s", w.Body.Bytes()) + + _, got := db.LastAnswer("u", "QID") + if got == (Answer{}) { + t.Error("no answer pushed:", got) + } + t.Logf("answer pushed: %+v", got) +}