impl many integration tests
parent
3b72f05b4e
commit
360a686906
4
main.go
4
main.go
|
|
@ -7,7 +7,9 @@ import (
|
|||
"log"
|
||||
)
|
||||
|
||||
var GitCommit string
|
||||
var (
|
||||
GitCommit string
|
||||
)
|
||||
|
||||
func main() {
|
||||
c := config.New()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,319 @@
|
|||
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
|
||||
}
|
||||
|
|
@ -61,14 +61,14 @@ func getKeyForNamespace(ctx context.Context, g storage.RateLimitedGraph, namespa
|
|||
func makeTokenForNamespace(ctx context.Context, g storage.RateLimitedGraph, namespace string) (Token, error) {
|
||||
token := Token{
|
||||
Namespace: namespace,
|
||||
Token: uuid.New().String(),
|
||||
ID: uuid.New().String(),
|
||||
}
|
||||
obf, err := token.Obfuscate()
|
||||
if err != nil {
|
||||
return Token{}, err
|
||||
}
|
||||
one := entity.One{
|
||||
ID: token.Token,
|
||||
ID: token.ID,
|
||||
Title: obf,
|
||||
}
|
||||
return token, g.Insert(ctx, namespace+"."+AuthKey, one)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,12 @@ import (
|
|||
|
||||
type Token struct {
|
||||
Namespace string
|
||||
Token string
|
||||
ID string
|
||||
}
|
||||
|
||||
func (t Token) String() string {
|
||||
s, _ := t.Obfuscate()
|
||||
return s
|
||||
}
|
||||
|
||||
func (t Token) Obfuscate() (string, error) {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import (
|
|||
func TestTokenEncDec(t *testing.T) {
|
||||
token := Token{
|
||||
Namespace: "username",
|
||||
Token: uuid.New().String(),
|
||||
ID: uuid.New().String(),
|
||||
}
|
||||
key := "a"
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ func getToken(r *http.Request) (Token, bool) {
|
|||
if !config.New().Auth {
|
||||
namespaces, ok := r.URL.Query()["ns"]
|
||||
if ok && len(namespaces) > 0 {
|
||||
return Token{Namespace: namespaces[0], Token: uuid.New().String()}, true
|
||||
return Token{Namespace: namespaces[0], ID: uuid.New().String()}, true
|
||||
}
|
||||
}
|
||||
cookie, err := r.Cookie(AuthKey)
|
||||
|
|
@ -55,7 +55,7 @@ func isPublicNamespace(ctx context.Context, g storage.RateLimitedGraph, namespac
|
|||
}
|
||||
|
||||
func verifyToken(token Token, g storage.RateLimitedGraph, r *http.Request) error {
|
||||
serverTokenContainer, err := g.Get(r.Context(), token.Namespace+"."+AuthKey, token.Token)
|
||||
serverTokenContainer, err := g.Get(r.Context(), token.Namespace+"."+AuthKey, token.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,12 +21,12 @@ func TestVerify(t *testing.T) {
|
|||
fresh := func() (storage.RateLimitedGraph, *httptest.ResponseRecorder, *http.Request, Token, string) {
|
||||
g := storage.NewRateLimitedGraph()
|
||||
token := Token{
|
||||
Token: uuid.New().String(),
|
||||
ID: uuid.New().String(),
|
||||
Namespace: uuid.New().String(),
|
||||
}
|
||||
obf, _ := token.Obfuscate()
|
||||
one := entity.One{
|
||||
ID: token.Token,
|
||||
ID: token.ID,
|
||||
Title: obf,
|
||||
}
|
||||
if err := g.Insert(context.TODO(), token.Namespace+"."+AuthKey, one); err != nil {
|
||||
|
|
@ -102,7 +102,7 @@ func TestVerify(t *testing.T) {
|
|||
|
||||
t.Run("bad auth", func(t *testing.T) {
|
||||
g, w, r, token, _ := fresh()
|
||||
token.Token = uuid.New().String()
|
||||
token.ID = uuid.New().String()
|
||||
obf, err := token.Obfuscate()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -133,7 +133,7 @@ func TestVerify(t *testing.T) {
|
|||
if err := g.Insert(context.TODO(), token.Namespace, entity.One{ID: UserKey}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
token.Token = "gibberish-but-public-ns-so-its-ok"
|
||||
token.ID = "gibberish-but-public-ns-so-its-ok"
|
||||
obf, _ := token.Obfuscate()
|
||||
r.AddCookie(&http.Cookie{
|
||||
Name: AuthKey,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ func NewREST(g storage.RateLimitedGraph) (*REST, error) {
|
|||
bar := foo
|
||||
bar = rest.shift(bar)
|
||||
bar = rest.scoped(bar)
|
||||
if !strings.HasPrefix(path, "users/") {
|
||||
if !strings.HasPrefix(path, "users/") && path != "version" {
|
||||
bar = rest.auth(bar)
|
||||
}
|
||||
bar = rest.defend(bar)
|
||||
|
|
|
|||
Loading…
Reference in New Issue