package fake import ( "fmt" "net/http" "net/http/httptest" "strconv" "strings" "time" "unicode" ) const ( RegistrationToken = "fake-registration-token" RunnersListBody = ` { "total_count": 2, "runners": [ {"id": 1, "name": "test1", "os": "linux", "status": "online", "busy": false}, {"id": 2, "name": "test2", "os": "linux", "status": "offline", "busy": false} ] } ` ) type ListRunnersHandler struct { Status int Body string } func (h *ListRunnersHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.WriteHeader(h.Status) fmt.Fprintf(w, "%s", h.Body) } type Handler struct { Status int Body string Statuses map[string]string } func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.WriteHeader(h.Status) status := req.URL.Query().Get("status") if h.Statuses != nil { if body, ok := h.Statuses[status]; ok { fmt.Fprint(w, body) return } } fmt.Fprintf(w, "%s", h.Body) } type MapHandler struct { Status int Bodies map[int]string } func (h *MapHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { // Parse out int key from URL path key, err := strconv.Atoi(strings.TrimFunc(req.URL.Path, func(r rune) bool { return !unicode.IsNumber(r) })) if err != nil { w.WriteHeader(400) } else if body := h.Bodies[key]; len(body) == 0 { w.WriteHeader(404) } else { w.WriteHeader(h.Status) fmt.Fprint(w, body) } } type ServerConfig struct { *FixedResponses } // NewServer creates a fake server for running unit tests func NewServer(opts ...Option) *httptest.Server { config := ServerConfig{ FixedResponses: &FixedResponses{}, } for _, o := range opts { o(&config) } routes := map[string]http.Handler{ // For CreateRegistrationToken "/repos/test/valid/actions/runners/registration-token": &Handler{ Status: http.StatusCreated, Body: fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)), }, "/repos/test/invalid/actions/runners/registration-token": &Handler{ Status: http.StatusOK, Body: fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)), }, "/repos/test/error/actions/runners/registration-token": &Handler{ Status: http.StatusBadRequest, Body: "", }, "/orgs/test/actions/runners/registration-token": &Handler{ Status: http.StatusCreated, Body: fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)), }, "/orgs/invalid/actions/runners/registration-token": &Handler{ Status: http.StatusOK, Body: fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)), }, "/orgs/error/actions/runners/registration-token": &Handler{ Status: http.StatusBadRequest, Body: "", }, "/enterprises/test/actions/runners/registration-token": &Handler{ Status: http.StatusCreated, Body: fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)), }, "/enterprises/invalid/actions/runners/registration-token": &Handler{ Status: http.StatusOK, Body: fmt.Sprintf("{\"token\": \"%s\", \"expires_at\": \"%s\"}", RegistrationToken, time.Now().Add(time.Hour*1).Format(time.RFC3339)), }, "/enterprises/error/actions/runners/registration-token": &Handler{ Status: http.StatusBadRequest, Body: "", }, // For ListRunners "/repos/test/valid/actions/runners": config.ListRunners, "/repos/test/invalid/actions/runners": &Handler{ Status: http.StatusNoContent, Body: "", }, "/repos/test/error/actions/runners": &Handler{ Status: http.StatusBadRequest, Body: "", }, "/orgs/test/actions/runners": &Handler{ Status: http.StatusOK, Body: RunnersListBody, }, "/orgs/invalid/actions/runners": &Handler{ Status: http.StatusNoContent, Body: "", }, "/orgs/error/actions/runners": &Handler{ Status: http.StatusBadRequest, Body: "", }, "/enterprises/test/actions/runners": &Handler{ Status: http.StatusOK, Body: RunnersListBody, }, "/enterprises/invalid/actions/runners": &Handler{ Status: http.StatusNoContent, Body: "", }, "/enterprises/error/actions/runners": &Handler{ Status: http.StatusBadRequest, Body: "", }, // For RemoveRunner "/repos/test/valid/actions/runners/0": &Handler{ Status: http.StatusNoContent, Body: "", }, "/repos/test/valid/actions/runners/1": &Handler{ Status: http.StatusNoContent, Body: "", }, "/repos/test/invalid/actions/runners/1": &Handler{ Status: http.StatusOK, Body: "", }, "/repos/test/error/actions/runners/1": &Handler{ Status: http.StatusBadRequest, Body: "", }, "/orgs/test/actions/runners/1": &Handler{ Status: http.StatusNoContent, Body: "", }, "/orgs/invalid/actions/runners/1": &Handler{ Status: http.StatusOK, Body: "", }, "/orgs/error/actions/runners/1": &Handler{ Status: http.StatusBadRequest, Body: "", }, "/enterprises/test/actions/runners/1": &Handler{ Status: http.StatusNoContent, Body: "", }, "/enterprises/invalid/actions/runners/1": &Handler{ Status: http.StatusOK, Body: "", }, "/enterprises/error/actions/runners/1": &Handler{ Status: http.StatusBadRequest, Body: "", }, // For auto-scaling based on the number of queued(pending) workflow runs "/repos/test/valid/actions/runs": config.ListRepositoryWorkflowRuns, // For auto-scaling based on the number of queued(pending) workflow jobs "/repos/test/valid/actions/runs/": config.ListWorkflowJobs, } mux := http.NewServeMux() for path, handler := range routes { mux.Handle(path, handler) } return httptest.NewServer(mux) } func DefaultListRunnersHandler() *ListRunnersHandler { return &ListRunnersHandler{ Status: http.StatusOK, Body: RunnersListBody, } }