├── LICENSE ├── Makefile ├── README.md ├── example ├── client │ └── main.go └── server │ └── main.go ├── gowork.go └── gowork_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: \ 2 | all \ 3 | deps \ 4 | update-deps \ 5 | test-deps \ 6 | update-test-deps \ 7 | build \ 8 | install \ 9 | lint \ 10 | vet \ 11 | errcheck \ 12 | pretest \ 13 | test \ 14 | cov \ 15 | clean 16 | 17 | all: test 18 | 19 | deps: 20 | go get -d -v ./... 21 | 22 | update-deps: 23 | go get -d -v -u -f ./... 24 | 25 | test-deps: 26 | go get -d -v -t ./... 27 | 28 | update-test-deps: 29 | go get -d -v -t -u -f ./... 30 | 31 | build: deps 32 | go build ./... 33 | 34 | install: deps 35 | go install ./... 36 | 37 | lint: test-deps 38 | go get -v github.com/golang/lint/golint 39 | golint ./. 40 | 41 | vet: test-deps 42 | go get -v golang.org/x/tools/cmd/vet 43 | go vet ./... 44 | 45 | errcheck: test-deps 46 | go get -v github.com/kisielk/errcheck 47 | errcheck ./... 48 | 49 | pretest: lint vet errcheck 50 | 51 | test: test-deps pretest 52 | go test -test.v ./... 53 | 54 | cov: test-deps 55 | go get -v github.com/axw/gocov/gocov 56 | go get golang.org/x/tools/cmd/cover 57 | gocov test | gocov report 58 | 59 | clean: 60 | go clean -i ./... 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GoWork 2 | 3 | [![GoDoc](https://godoc.org/github.com/ryanskidmore/GoWork?status.svg)](https://godoc.org/github.com/ryanskidmore/GoWork) 4 | 5 | GoWork is a library for the encapsulation and delivery of Work to a distributed set of Workers. 6 | 7 | ## Installation 8 | 9 | `go get github.com/ryanskidmore/gowork` 10 | 11 | #### NOTE: Only tested with Go 1.3+ 12 | 13 | ## Features 14 | 15 | * Worker Registration/Authentication 16 | * Work Encapsulation and Tracking 17 | * Built-in Work Queue 18 | * Event based handler functions (e.g. run function when work complete, etc) 19 | 20 | ## Usage 21 | 22 | ### Server 23 | 24 | #### Work 25 | 26 | Generate a secret:, 27 | ```go 28 | secret, err := gowork.GenerateSecret() 29 | ``` 30 | Error is returned when the secret isn't exactly 32 characters long. 31 | 32 | Then create a new work server, 33 | ```go 34 | ws, err := gowork.NewServer(secret) 35 | ``` 36 | Next, you can add parameters to be passed to your handler functions, though this is entirely optional. 37 | ```go 38 | ws = ws.AddParams(map[string]interface{}{"param1":"value1"}) 39 | ``` 40 | 41 | Then, you can add handler functions to be called when certain events happen. 42 | ```go 43 | err = ws.NewHandler("event_id", func(e *Event, params map[string]interface{}){// function //}) 44 | ``` 45 | This function will only return an error when the handler already exists, so in most situations you can probably ignore the err returned. 46 | 47 | * Event ID's listed at the end of this section 48 | * Event contains data about the event, and is of type gowork.Event. Contains data about Work, Worker, Error and timings. 49 | * Params passed in earlier will be available in the params map (however, you will need to cast them into the appropriate type). 50 | 51 | Now work can be created and added. 52 | ```go 53 | w, err := gowork.CreateWork(WorkData interface{}, Timeout int64) 54 | ws.Add(w) 55 | ``` 56 | WorkData can be anything that you want to pass to the worker. Timeout is duration in seconds in integer64 type until you want the work to Timeout. 57 | Error is returned by `gowork.CreateWork()` when the WorkData fails to marshal into JSON format. 58 | 59 | At this point, the work is created and added to the work queue. 60 | 61 | The work is able to be retrieved then by calling the function `Get` 62 | ```go 63 | w, err := ws.Get("Worker ID", "Worker Authentication String") 64 | ``` 65 | The Worker ID and Authentication String are provided to the worker by functions below in the Workers section. 66 | 67 | This function will return an error when:- 68 | 69 | * The Worker ID cannot be converted to an integer 70 | * The Work has timed out 71 | * The Worker has failed to authenticate 72 | 73 | When there is no work, the function will return an empty Work object and no error. 74 | 75 | Then the work can be marshalled to be transferred to the worker (via a means of your choice) using the function `Marshal()` on the Work Object 76 | 77 | ```go 78 | workstring := w.Marshal() 79 | ``` 80 | 81 | To accompany this function, there is also an unmarshal function to convert the string to a work object 82 | 83 | ```go 84 | w := Unmarshal(workstring) 85 | ``` 86 | 87 | Once the worker has completed the work and passed it back to the server, it can be submitted via the `Submit()` function which marks the work as complete and calls the `work_complete` event. 88 | 89 | ```go 90 | ws.Submit(w) 91 | ``` 92 | 93 | Should you wish to get the size of the work queue, there is a function available to return this. 94 | 95 | ```go 96 | size := ws.QueueSize() 97 | ``` 98 | 99 | ##### Event IDs 100 | * add\_handler_error 101 | * add_work 102 | * get_work 103 | * get\_work_empty 104 | * get\_work_error 105 | * work_complete 106 | * work_timeout 107 | * worker_registered 108 | * worker_verify 109 | * worker\_verify_error 110 | 111 | #### Workers 112 | 113 | When first receiving contact from the worker, the worker needs to be regisered. This provides the worker with an ID as well as a randomly generated string to encrypt in order to verify the secrets are the same on the client and the server, as well as to authenticate the worker. 114 | 115 | ```go 116 | id, clienttest := ws.Workers.Register(ws) 117 | ``` 118 | 119 | The client then calls the appropriate functions on these strings and returns the encrypted string (client response) for verification to the server. 120 | 121 | ```go 122 | authkey, err := ws.Workers.Verify(ws, id, clientresponse) 123 | ``` 124 | This function provides a session authentication key for the worker to use. Returns an error when:- 125 | 126 | * Couldn't Worker ID to Int 127 | * Failed to decrypt the client response 128 | * Client Key is incorrect 129 | 130 | ### Client 131 | 132 | When creating a new worker, the worker must first contact the work server and get an ID and Client Test before calling any Go Work functions. Once these two strings have been obtained, the following function can be called. 133 | 134 | ```go 135 | Worker, err := gowork.NewWorker(Secret, Id, ClientTest) 136 | ``` 137 | 138 | This function will return an error when:- 139 | 140 | * Secret is not 32 characters 141 | * Couldn't convert Worker ID to Int 142 | * Failed to encrypt the client test 143 | 144 | This function will then store the client response in `Worker.Verification.ClientResponse`, which must then be passed to the server for verification, which will then return a session authentication key. 145 | 146 | This key can be set within the worker by calling:- 147 | 148 | ```go 149 | Worker = Worker.SetAuthenticationKey(Key) 150 | ``` 151 | 152 | Then the worker is ready to get work, this must be done independently of the library, using the Worker.Id and Worker.SessionAuthenticationKey strings for authentication. Once the work is retrieved, it can be unmarshalled into a work object and then passed into `Process` 153 | 154 | ```go 155 | w, workdata, err := Worker.Process(w) 156 | ``` 157 | 158 | This function returns the original work object as well as the work data in a map of interfaces. An error will be returned when:- 159 | 160 | * The work data cannot be unmarshalled 161 | * The work has timed out 162 | 163 | Once the worker has completed the work, it can be submitted using `Submit` 164 | 165 | ```go 166 | w, err := Worker.Submit(w, result, error) 167 | ``` 168 | 169 | This function takes the original work object, appends the result to it (string) as well as any error (string) and returns the new work object back ready for marshalling and submission to the server. An error will be returned when the work has timed out. 170 | 171 | ## Handler Functions 172 | A quick note on handlers, they're intended to act as middleware - so an ideal usage scenario would be adding a piece of work to a database when it's complete, for example. 173 | 174 | ## Contributors 175 | 176 | Ryan Skidmore - [github.com/ryanskidmore](http://github.com/ryanskidmore) 177 | -------------------------------------------------------------------------------- /example/client/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/levigross/grequests" 6 | "github.com/ryanskidmore/GoWork" 7 | "strings" 8 | ) 9 | 10 | func main() { 11 | response, err := grequests.Get("http://127.0.0.1:3000/register", nil) 12 | if err != nil { 13 | panic("Unable to register:" + err.Error()) 14 | } 15 | respdata := strings.Split(response.String(), ",") 16 | id := respdata[0] 17 | clienttest := respdata[1] 18 | worker, err := gowork.NewWorker("w4PYxQjVP9ZStjWpBt5t28CEBmRs8NPx", id, clienttest) 19 | testresponse, err := grequests.Post("http://127.0.0.1:3000/verify", &grequests.RequestOptions{Params: map[string]string{"id": id, "clientresp": worker.Verification.ClientResponse}}) 20 | if err != nil { 21 | panic("Unable to verify:" + err.Error()) 22 | } 23 | worker = worker.SetAuthenticationKey(testresponse.String()) 24 | fmt.Println(worker.SessionAuthenticationKey) 25 | testresponse, err = grequests.Post("http://127.0.0.1:3000/get_work", &grequests.RequestOptions{Params: map[string]string{"id": id, "sessionauthkey": worker.SessionAuthenticationKey}}) 26 | fmt.Println(testresponse.String()) 27 | } 28 | -------------------------------------------------------------------------------- /example/server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/go-martini/martini" 6 | "github.com/ryanskidmore/GoWork" 7 | "net/http" 8 | ) 9 | 10 | func main() { 11 | ws, _ := gowork.NewServer("w4PYxQjVP9ZStjWpBt5t28CEBmRs8NPx") 12 | m := martini.Classic() 13 | m.Get("/register", func() string { 14 | id, clienttest := ws.Workers.Register(ws) 15 | return id + "," + clienttest 16 | }) 17 | m.Post("/verify", func(req *http.Request) string { 18 | id := req.FormValue("id") 19 | clientresp := req.FormValue("clientresp") 20 | auth, err := ws.Workers.Verify(ws, id, clientresp) 21 | fmt.Println(err) 22 | return auth 23 | }) 24 | m.Post("/get_work", func(req *http.Request) string { 25 | w, err := ws.Get(req.FormValue("id"), req.FormValue("sessionauthkey")) 26 | fmt.Println(err) 27 | return w.Marshal() 28 | }) 29 | m.Run() 30 | } 31 | -------------------------------------------------------------------------------- /gowork.go: -------------------------------------------------------------------------------- 1 | package gowork 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "strconv" 8 | "time" 9 | 10 | "github.com/oleiade/lane" 11 | "github.com/satori/go.uuid" 12 | "go.pedge.io/encrypt" 13 | "gopkg.in/mgo.v2/bson" 14 | ) 15 | 16 | type WorkServer struct { 17 | Queue *lane.Queue 18 | Handlers map[string]func(*Event, map[string]interface{}) 19 | HandlerParams map[string]interface{} 20 | Workers *WorkersStruct 21 | } 22 | 23 | type WorkersStruct struct { 24 | Members map[int]*Worker 25 | Transformer encrypt.Transformer 26 | WorkerCount int 27 | } 28 | 29 | type Worker struct { 30 | Id int 31 | Registered bool 32 | Transformer encrypt.Transformer 33 | SessionAuthenticationKey string 34 | Verification *ClientTest 35 | } 36 | 37 | type ClientTest struct { 38 | PlaintextVerification string `json:"Verification"` 39 | ClientResponse string `json:"Response"` 40 | } 41 | 42 | type Work struct { 43 | Id bson.ObjectId `json:"-" bson:"_id"` 44 | IdHex string 45 | WorkJSON string 46 | Result *WorkResult 47 | Time *TimeStats 48 | } 49 | 50 | type WorkResult struct { 51 | ResultJSON string 52 | Status string 53 | Error string 54 | } 55 | 56 | type TimeStats struct { 57 | Added int64 58 | Recieved int64 59 | Complete int64 60 | Timeout int64 61 | } 62 | 63 | type Event struct { 64 | Work *Work 65 | Worker *Worker 66 | Error string 67 | Time int64 68 | } 69 | 70 | func GenerateSecret() (string, error) { 71 | EncodedSecret, err := encrypt.GenerateAESKey(encrypt.AES256Bits) 72 | if err != nil { 73 | return "", err 74 | } 75 | Secret, err := encrypt.DecodeString(EncodedSecret) 76 | if err != nil { 77 | return "", err 78 | } 79 | return string(Secret), nil 80 | } 81 | 82 | func NewEventError(msg string) *Event { 83 | return &Event{Error: msg, Time: time.Now().UTC().Unix()} 84 | } 85 | 86 | func NewEventWork(w *Work) *Event { 87 | return &Event{Work: w, Time: time.Now().UTC().Unix()} 88 | } 89 | 90 | func NewEventWorker(w *Worker) *Event { 91 | return &Event{Worker: w, Time: time.Now().UTC().Unix()} 92 | } 93 | 94 | func NewServer(Secret string) (*WorkServer, error) { 95 | Transformer, err := NewTransformer(Secret) 96 | if err != nil { 97 | return &WorkServer{}, err 98 | } 99 | return NewServerInit(Transformer), nil 100 | } 101 | 102 | func MustNewServer(Secret string) *WorkServer { 103 | return NewServerInit(MustNewTransformer(Secret)) 104 | } 105 | 106 | func NewServerInit(Transformer encrypt.Transformer) *WorkServer { 107 | Queue := lane.NewQueue() 108 | WorkerMembers := make(map[int]*Worker) 109 | Workers := &WorkersStruct{WorkerMembers, Transformer, 0} 110 | HandlerFuncs := make(map[string]func(*Event, map[string]interface{})) 111 | HandlerParams := make(map[string]interface{}) 112 | WorkServerInst := &WorkServer{Queue, HandlerFuncs, HandlerParams, Workers} 113 | return WorkServerInst 114 | } 115 | 116 | func (ws *WorkServer) NewHandler(event_id string, hf func(*Event, map[string]interface{})) error { 117 | if _, exists := ws.Handlers[event_id]; exists { 118 | ws.Event("add_handler_error", NewEventError("HandlerExists")) 119 | return errors.New("Handler already exists") 120 | } 121 | ws.Handlers[event_id] = hf 122 | return nil 123 | } 124 | 125 | func (ws *WorkServer) AddParams(params map[string]interface{}) *WorkServer { 126 | ws.HandlerParams = params 127 | return ws 128 | } 129 | 130 | func (ws *WorkServer) Event(event_id string, event *Event) { 131 | if handlerFunc, exists := ws.Handlers[event_id]; exists { 132 | handlerFunc(event, ws.HandlerParams) 133 | } 134 | } 135 | 136 | func (ws *WorkServer) Add(w *Work) { 137 | w.Time.Added = time.Now().UTC().Unix() 138 | ws.Event("add_work", NewEventWork(w)) 139 | ws.Queue.Enqueue(w) 140 | } 141 | 142 | func (ws *WorkServer) Get(Id string, AuthenticationKey string) (*Work, error) { 143 | IdInt, err := strconv.Atoi(Id) 144 | if err != nil { 145 | ws.Event("get_work_error", NewEventError("StrconvError")) 146 | return &Work{}, errors.New("Failed to convert Worker ID string to int:" + err.Error()) 147 | } 148 | if ws.Workers.Members[IdInt].SessionAuthenticationKey != AuthenticationKey { 149 | ws.Event("get_work_error", NewEventError("AuthFailed")) 150 | return &Work{}, errors.New("Failed authentication") 151 | } 152 | WorkObj := ws.Queue.Dequeue() 153 | if WorkObj == nil { 154 | ws.Event("get_work_empty", NewEventError("NoWork")) 155 | return &Work{}, nil 156 | } 157 | if (WorkObj.(*Work).Time.Added + WorkObj.(*Work).Time.Timeout) > time.Now().UTC().Unix() { 158 | ws.Event("get_work", NewEventWork(WorkObj.(*Work))) 159 | return WorkObj.(*Work), nil 160 | } 161 | ws.Event("work_timeout", NewEventWork(WorkObj.(*Work))) 162 | return WorkObj.(*Work), errors.New("Work Timeout") 163 | } 164 | 165 | func (ws *WorkServer) Submit(w *Work) { 166 | if (w.Time.Added + w.Time.Timeout) <= time.Now().UTC().Unix() { 167 | w.Result.Error = "Timeout" 168 | w.Result.Status = "Timeout" 169 | ws.Event("work_timeout", NewEventWork(w)) 170 | return 171 | } 172 | w.Id = bson.ObjectIdHex(w.IdHex) 173 | ws.Event("work_complete", NewEventWork(w)) 174 | } 175 | 176 | func (ws *WorkServer) QueueSize() int { 177 | return ws.Queue.Size() 178 | } 179 | 180 | func (wrs *WorkersStruct) Register(ws *WorkServer) (string, string) { 181 | TempWC := wrs.WorkerCount 182 | wrs.WorkerCount += 1 183 | w := &Worker{ 184 | Id: TempWC + 1, 185 | Verification: &ClientTest{PlaintextVerification: uuid.NewV4().String()}, 186 | Registered: false, 187 | } 188 | wrs.Members[w.Id] = w 189 | ws.Event("worker_register", NewEventWorker(w)) 190 | return strconv.Itoa(w.Id), w.Verification.PlaintextVerification 191 | } 192 | 193 | func (wrs *WorkersStruct) Verify(ws *WorkServer, Id string, Response string) (string, error) { 194 | IdInt, err := strconv.Atoi(Id) 195 | if err != nil { 196 | ws.Event("worker_verify_error", NewEventError("StrconvError")) 197 | return "", errors.New("Failed to convert Worker ID string to int:" + err.Error()) 198 | } 199 | ClientResp, err := wrs.Transformer.Decrypt([]byte(Response)) 200 | if err != nil { 201 | ws.Event("worker_verify_error", NewEventError("DecryptionError")) 202 | return "", errors.New("Failed to decrypt worker verification string:" + err.Error()) 203 | } 204 | wrs.Members[IdInt].Verification.ClientResponse = string(ClientResp) 205 | if wrs.Members[IdInt].Verification.PlaintextVerification != string(wrs.Members[IdInt].Verification.ClientResponse) { 206 | ws.Event("worker_verify_error", NewEventError("KeyMismatch")) 207 | return "", errors.New("Client key incorrect") 208 | } 209 | wrs.Members[IdInt].Registered = true 210 | wrs.Members[IdInt].SessionAuthenticationKey = uuid.NewV4().String() 211 | ws.Event("worker_verify", NewEventWorker(wrs.Members[IdInt])) 212 | return wrs.Members[IdInt].SessionAuthenticationKey, nil 213 | } 214 | 215 | func NewWorker(Secret string, ID string, PlaintextVerification string) (*Worker, error) { 216 | wrk := &Worker{} 217 | Transformer, err := NewTransformer(Secret) 218 | if err != nil { 219 | return wrk, err 220 | } 221 | wrk.Transformer = Transformer 222 | wrk.Verification = &ClientTest{PlaintextVerification: PlaintextVerification} 223 | IdInt, err := strconv.Atoi(ID) 224 | if err != nil { 225 | return &Worker{}, errors.New("Failed to convert Worker ID string to int:" + err.Error()) 226 | } 227 | wrk.Id = IdInt 228 | ClientResponse, err := wrk.Transformer.Encrypt([]byte(wrk.Verification.PlaintextVerification)) 229 | if err != nil { 230 | return &Worker{}, errors.New("Failed to encrypt verification string:" + err.Error()) 231 | } 232 | wrk.Verification.ClientResponse = string(ClientResponse) 233 | return wrk, nil 234 | } 235 | 236 | func (wrk *Worker) SetAuthenticationKey(key string) *Worker { 237 | wrk.SessionAuthenticationKey = key 238 | return wrk 239 | } 240 | 241 | func (wrk *Worker) Process(w *Work) (*Work, map[string]interface{}, error) { 242 | WorkParams := make(map[string]interface{}) 243 | if (w.Time.Added + w.Time.Timeout) <= time.Now().UTC().Unix() { 244 | return w, WorkParams, errors.New("Work Timeout") 245 | } 246 | err := json.Unmarshal([]byte(w.WorkJSON), &WorkParams) 247 | if err != nil { 248 | return w, WorkParams, errors.New("Failed to unmarshal Work Params JSON:" + err.Error()) 249 | } 250 | w.Time.Recieved = time.Now().UTC().Unix() 251 | return w, WorkParams, nil 252 | } 253 | 254 | func (wrk *Worker) Submit(w *Work, ResultJSON string, Error string) (*Work, error) { 255 | wr := &WorkResult{} 256 | wr.ResultJSON = ResultJSON 257 | w.Time.Complete = time.Now().UTC().Unix() 258 | if (w.Time.Added + w.Time.Timeout) > time.Now().UTC().Unix() { 259 | wr.Error = Error 260 | wr.Status = "Complete" 261 | w.Result = wr 262 | return w, nil 263 | } 264 | wr.Error = "Timeout" 265 | wr.Status = "Timeout" 266 | w.Result = wr 267 | return w, errors.New("Timeout") 268 | } 269 | 270 | func CreateWork(WorkData interface{}, Timeout int64) (*Work, error) { 271 | NewWork := &Work{} 272 | NewWork.IdHex = bson.NewObjectId().Hex() 273 | NewWork.Result = &WorkResult{"", "Pending", ""} 274 | NewWork.Time = &TimeStats{Timeout: Timeout} 275 | WorkDataJSON, err := json.Marshal(WorkData) 276 | if err != nil { 277 | return &Work{}, errors.New("Failed to marshal work data:" + err.Error()) 278 | } 279 | NewWork.WorkJSON = string(WorkDataJSON) 280 | return NewWork, nil 281 | } 282 | 283 | func (w *Work) Marshal() string { 284 | MarshalledWork, _ := json.Marshal(w) 285 | return string(MarshalledWork) 286 | } 287 | 288 | func Unmarshal(w string) *Work { 289 | WorkObject := &Work{} 290 | _ = json.Unmarshal([]byte(w), &WorkObject) 291 | return WorkObject 292 | } 293 | 294 | func NewTransformer(Secret string) (encrypt.Transformer, error) { 295 | if len(Secret) != 32 { 296 | return nil, fmt.Errorf("Length of secret must be 32, length was %d", len(Secret)) 297 | } 298 | return encrypt.NewAESTransformer(encrypt.EncodeToString([]byte(Secret))) 299 | } 300 | 301 | func MustNewTransformer(Secret string) encrypt.Transformer { 302 | Transformer, err := NewTransformer(Secret) 303 | if err != nil { 304 | panic(err.Error()) 305 | } 306 | return Transformer 307 | } 308 | -------------------------------------------------------------------------------- /gowork_test.go: -------------------------------------------------------------------------------- 1 | package gowork 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | const ( 9 | ERROR_MSG string = "AHH ERROR HAPPENED" 10 | EVENT_ID string = "123abc" 11 | SECRET_STR_INVALID string = "GoWork" 12 | SECRET_STR_VALID string = "GoWorkGoWorkGoWorkGoWorkGoWork12" 13 | ) 14 | 15 | func TestNewEventError(t *testing.T) { 16 | var ( 17 | ofType string 18 | ) 19 | 20 | t.Parallel() 21 | 22 | e := NewEventError(ERROR_MSG) 23 | ofType = reflect.TypeOf(e).String() 24 | 25 | if ofType != "*gowork.Event" { 26 | t.Fatalf("Expected type of *Event, received %s instead.", ofType) 27 | } 28 | 29 | if e.Error != ERROR_MSG { 30 | t.Fatalf("Expected error message %s, received %s instead.", ERROR_MSG, e.Error) 31 | } 32 | } 33 | 34 | func TestNewEventWork(t *testing.T) { 35 | var ( 36 | ofType string 37 | ) 38 | 39 | t.Parallel() 40 | 41 | w := NewEventWork(&Work{}) 42 | ofType = reflect.TypeOf(w).String() 43 | 44 | if ofType != "*gowork.Event" { 45 | t.Fatalf("Expected type of *Event, received %s instead.", ofType) 46 | } 47 | } 48 | 49 | func TestNewEventWorker(t *testing.T) { 50 | var ( 51 | ofType string 52 | ) 53 | 54 | t.Parallel() 55 | 56 | w := NewEventWorker(&Worker{}) 57 | ofType = reflect.TypeOf(w).String() 58 | 59 | if ofType != "*gowork.Event" { 60 | t.Fatalf("Expected type of *Event, received %s instead.", ofType) 61 | } 62 | } 63 | 64 | func TestNewServer(t *testing.T) { 65 | var ( 66 | err error 67 | ) 68 | 69 | t.Parallel() 70 | 71 | if _, err = NewServer(SECRET_STR_VALID); err != nil { 72 | t.Fatal(err) 73 | } 74 | } 75 | 76 | func TestNewServerInvalidSecretSize(t *testing.T) { 77 | var ( 78 | err error 79 | ) 80 | 81 | t.Parallel() 82 | 83 | if _, err = NewServer(SECRET_STR_INVALID); err == nil { 84 | t.Fatalf("Expected NewServer to throw a secret length error. Secret %s was passed in.", SECRET_STR_INVALID) 85 | } 86 | } 87 | 88 | func TestMustNewServer(t *testing.T) { 89 | defer func() { 90 | if r := recover(); r != nil { 91 | t.Fatalf("TestMustNewServer paniced when it shouldn't have. Recovered %v", r) 92 | } 93 | }() 94 | 95 | t.Parallel() 96 | MustNewServer(SECRET_STR_VALID) 97 | } 98 | 99 | func TestMustNewServerPanics(t *testing.T) { 100 | defer func() { 101 | if r := recover(); r != nil { 102 | return 103 | } else { 104 | t.Fatalf("TestMustNewServer did not panic when it should have. Secret %s was passed in.", SECRET_STR_INVALID) 105 | } 106 | }() 107 | 108 | t.Parallel() 109 | MustNewServer(SECRET_STR_INVALID) 110 | } 111 | 112 | func TestNewHandler(t *testing.T) { 113 | var ( 114 | err error 115 | ws *WorkServer = MustNewServer(SECRET_STR_VALID) 116 | ) 117 | 118 | t.Parallel() 119 | 120 | if err = ws.NewHandler(EVENT_ID, func(*Event, map[string]interface{}) {}); err != nil { 121 | t.Fatal(err) 122 | } 123 | 124 | if _, ok := ws.Handlers[EVENT_ID]; !ok { 125 | t.Fatalf("Expected event_id %s to be set as a handler, it was not", EVENT_ID) 126 | } 127 | } 128 | 129 | func TestNewHandlerAlreadyExists(t *testing.T) { 130 | var ( 131 | err error 132 | ws *WorkServer = MustNewServer(SECRET_STR_VALID) 133 | ) 134 | 135 | t.Parallel() 136 | 137 | if err = ws.NewHandler(EVENT_ID, func(*Event, map[string]interface{}) {}); err != nil { 138 | t.Fatal(err) 139 | } 140 | 141 | if err = ws.NewHandler(EVENT_ID, func(*Event, map[string]interface{}) {}); err == nil { 142 | t.Fatalf("Expected NewHandler to throw a 'Handler already exists' error. Event ID %s was passed in.", EVENT_ID) 143 | } 144 | } 145 | 146 | func TestAddParams(t *testing.T) { 147 | var ( 148 | ws *WorkServer = MustNewServer(SECRET_STR_VALID) 149 | params map[string]interface{} = map[string]interface{}{ 150 | "key": "value", 151 | "key2": 12, 152 | } 153 | ) 154 | 155 | t.Parallel() 156 | 157 | ws = ws.AddParams(params) 158 | 159 | if ws.HandlerParams["key"].(string) != "value" { 160 | t.Fatal("AddParams failed to set HandlerParams properly.") 161 | } 162 | 163 | if ws.HandlerParams["key2"].(int) != 12 { 164 | t.Fatal("AddParams failed to set HandlerParams properly.") 165 | } 166 | } 167 | 168 | func TestQueueSize(t *testing.T) { 169 | var ( 170 | ws *WorkServer = MustNewServer(SECRET_STR_VALID) 171 | ) 172 | 173 | if ws.QueueSize() != ws.Queue.Size() { 174 | t.Fatalf("Expected QueueSize to return %d, it returned %d instead.", ws.Queue.Size(), ws.QueueSize()) 175 | } 176 | } 177 | --------------------------------------------------------------------------------