package main import ( "bytes" "encoding/json" "fmt" "io" "io/ioutil" "local/dndex/server/auth" "local/dndex/storage/entity" "net/http" "net/http/httptest" "os" "strings" "testing" "time" "github.com/google/uuid" ) func Test(t *testing.T) { GitCommit = uuid.New().String() d, err := ioutil.TempDir(os.TempDir(), "test.dndex") if err != nil { t.Fatal(err) } s := httptest.NewServer(http.HandlerFunc(nil)) s.Close() p := strings.Split(s.URL, ":")[2] os.Args = strings.Split(fmt.Sprintf(`dndex -auth=true -database db -delay 5ms -driver-type map -fileprefix /files -fileroot %s -p %v -rps 50 -sys-rps 40`, d, p), " ") go main() for { resp, err := http.Get(s.URL) if err == nil { resp.Body.Close() break } time.Sleep(time.Millisecond * 100) } token := register(t, s.URL) t.Logf("token: %q", token) createDelete(t, s.URL, token) createUpdate(t, s.URL, token) callVersion(t, s.URL) muckedToken(t, s.URL, token) createDeleteSub(t, s.URL, token) createUpdateSub(t, s.URL, token) filesCRUD(t, s.URL, token) } func createDeleteSub(t *testing.T, uri, token string) { t.Error("not impl") } func createUpdateSub(t *testing.T, uri, token string) { t.Error("not impl") } func filesCRUD(t *testing.T, uri, token string) { t.Error("not impl") } func register(t *testing.T, uri string) string { callList(t, uri, "", http.StatusUnauthorized) ns, pwd := callRegister(t, uri) token := callLogin(t, uri, ns, pwd) callList(t, uri, token, http.StatusOK) return token } func muckedToken(t *testing.T, uri, obf string) { var token auth.Token if err := token.Deobfuscate(obf); err != nil { t.Fatal(err) } if token.Namespace == "" || token.ID == "" { t.Fatalf("ns: %q, id: %q", token.Namespace, token.ID) } namespace := token.Namespace id := token.ID for _, token := range []auth.Token{ auth.Token{ Namespace: namespace, ID: uuid.New().String(), }, auth.Token{ Namespace: uuid.New().String(), ID: id, }, } { callList(t, uri, token.String(), http.StatusUnauthorized) } } func createUpdate(t *testing.T, uri, token string) { id := callCreate(t, uri, token) callUpdate(t, uri, token, id) } func createDelete(t *testing.T, uri, token string) { id := callCreate(t, uri, token) callDelete(t, uri, token, id) one := callGet(t, uri, token, id, http.StatusNotFound) if one.ID == id { t.Fatal(one) } if fmt.Sprint(one) != fmt.Sprint(entity.One{}) { t.Fatal(one) } } func callVersion(t *testing.T, uri string) { resp := call(t, "", http.MethodGet, uri+"/version", "") if resp.StatusCode != http.StatusOK { t.Fatal(resp.StatusCode) } var response struct { Version string `json:"version"` } b, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } if err := json.Unmarshal(b, &response); err != nil { t.Fatal(err) } if response.Version != GitCommit { t.Fatalf("%s: %v", b, response) } } func callGet(t *testing.T, uri, token, id string, status int) entity.One { resp := call(t, token, http.MethodGet, uri+"/entities/"+id, "") if resp.StatusCode != status { t.Fatal(resp.StatusCode) } var one entity.One if err := json.NewDecoder(resp.Body).Decode(&one); err != nil { t.Fatal(err) } return one } func callDelete(t *testing.T, uri, token, id string) { resp := call(t, token, http.MethodDelete, uri+"/entities/"+id, "") if resp.StatusCode != http.StatusOK { t.Fatal(resp.StatusCode) } } func callUpdate(t *testing.T, uri, token, id string) { for _, method := range []string{http.MethodPut, http.MethodPatch} { m := map[string]interface{}{ "name": "name-" + uuid.New().String()[:5], "type": "type-" + uuid.New().String()[:5], "title": "titl-" + uuid.New().String()[:5], "text": "text-" + uuid.New().String()[:5], "connections": map[string]map[string]string{ "???": map[string]string{"relationship": ":?"}, }, "attachments": map[string]map[string]string{ "myfile": map[string]string{"location": "/files/my_file_location.txt"}, }, } b, err := json.Marshal(m) if err != nil { t.Fatal(err) } resp := call(t, token, method, uri+"/entities/"+id, string(b)) if resp.StatusCode != http.StatusOK { t.Fatal(resp.StatusCode) } var response map[string]map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { t.Fatal(err) } found := false for k := range response { for k2 := range m { if fmt.Sprint(m[k2]) != fmt.Sprint(response[k][k2]) { t.Fatal(k2, response[k][k2]) } } found = true } if !found { t.Fatal(found) } } } func callCreate(t *testing.T, uri, token string) string { m := map[string]interface{}{ "name": "name-" + uuid.New().String()[:5], "type": "type-" + uuid.New().String()[:5], "title": "titl-" + uuid.New().String()[:5], "text": "text-" + uuid.New().String()[:5], "connections": map[string]map[string]string{ "???": map[string]string{"relationship": ":?"}, }, "attachments": map[string]map[string]string{ "myfile": map[string]string{"location": "/files/my_file_location.txt"}, }, } b, err := json.Marshal(m) if err != nil { t.Fatal(err) } resp := call(t, token, http.MethodPost, uri+"/entities", string(b)) if resp.StatusCode != http.StatusOK { t.Fatal(resp.StatusCode) } var response map[string]map[string]interface{} if err := json.NewDecoder(resp.Body).Decode(&response); err != nil { t.Fatal(err) } for k := range response { for k2 := range m { if fmt.Sprint(m[k2]) != fmt.Sprint(response[k][k2]) { t.Fatal(k2, response[k][k2]) } } return k } t.Fatal(response) panic("how?") } func callList(t *testing.T, uri, token string, status int) { resp := call(t, token, http.MethodGet, uri+"/entities", "") b, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } if resp.StatusCode != status { t.Fatalf("%v: %s", resp.StatusCode, b) } } func callRegister(t *testing.T, uri string) (string, string) { ns := uuid.New().String() pwd := uuid.New().String() resp, err := http.Post(uri+"/users/register", "application/x-www-form-urlencoded", strings.NewReader(fmt.Sprintf("%s=%s&%s=%s", auth.UserKey, ns, auth.AuthKey, pwd))) if err != nil { t.Fatal(err) } resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatal(resp.StatusCode) } return ns, pwd } func callLogin(t *testing.T, uri, ns, pwd string) string { resp, err := http.Post(uri+"/users/login", "application/x-www-form-urlencoded", strings.NewReader(fmt.Sprintf("%s=%s", auth.UserKey, ns))) if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Fatal(resp.StatusCode) } b, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } var response struct { OK struct { EncodedToken string `json:"token"` Salt string `json:"salt"` } `json:"ok"` } if err := json.Unmarshal(b, &response); err != nil { t.Fatalf("%v: %s", err, b) } salt := response.OK.Salt encodedToken := response.OK.EncodedToken if len(salt) == 0 { t.Fatal("salt empty") } if len(encodedToken) == 0 { t.Fatal("token empty") } token := auth.Token{} if err := token.Decode(salt+pwd, encodedToken); err != nil { t.Fatal(err) } return token.String() } func call(t *testing.T, token, method, uri, body string) *http.Response { r, err := http.NewRequest(method, uri, strings.NewReader(body)) if err != nil { t.Fatal(err) } r.AddCookie(&http.Cookie{Name: auth.AuthKey, Value: token}) resp, err := http.DefaultClient.Do(r) if err != nil { t.Fatal(err) } defer resp.Body.Close() b, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } resp.Body = struct { io.Reader io.Closer }{ Closer: resp.Body, Reader: bytes.NewReader(b), } return resp }