From 4c086910cbcb43dc7e4d81e6ddbf61a3ff36fd54 Mon Sep 17 00:00:00 2001 From: Sergey Zhemoytel Date: Mon, 9 Dec 2024 00:44:37 +0300 Subject: [PATCH] add code --- .gitignore | 5 + README.md | 2 +- abfapi.go | 629 +++++++++++++++++++++++++++++++++++++++++++++++++ abfapi_test.go | 252 ++++++++++++++++++++ go.mod | 16 ++ go.sum | 23 ++ 6 files changed, 926 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 abfapi.go create mode 100644 abfapi_test.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..264bb93 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.*~ +*~ +.env +cache.json +bin/* diff --git a/README.md b/README.md index 9384480..092a911 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,4 @@ Пакет golang для ABF API -Пакет основан на кодовой базе (abf-console-client)[https://github.com/OpenMandrivaSoftware/abf-console-client] +Пакет основан на кодовой базе [abf-console-client](https://github.com/OpenMandrivaSoftware/abf-console-client) diff --git a/abfapi.go b/abfapi.go new file mode 100644 index 0000000..c31d2f6 --- /dev/null +++ b/abfapi.go @@ -0,0 +1,629 @@ +package abfapi + +import ( + "bytes" + "crypto/sha1" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "io/ioutil" + "log" + "mime/multipart" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + "time" + + "github.com/gregjones/httpcache" + "github.com/gregjones/httpcache/diskcache" +) + +const ( + maxFileSize = 32 * 1024 * 1024 + blockSize = 1024 * 1024 +) + +var symbols = map[string][]string{ + "basic": {"b", "k", "m", "g", "t"}, + "basic_long": {"byte", "kilo", "mega", "giga", "tera"}, + "iec": {"bi", "ki", "mi", "gi", "ti"}, + "iec_long": {"byte", "kibi", "mebi", "gibi", "tebi"}, +} + +type AbfJson struct { + login string + password string + abfURL string + fileStoreURL string + base64AuthStr string + log *log.Logger + client *http.Client +} + +func NewAbfJson(abfURL, fileStoreURL, login, password string, logger *log.Logger) (*AbfJson, error) { + if !strings.HasPrefix(fileStoreURL, "http://") && !strings.HasPrefix(fileStoreURL, "https://") { + return nil, errors.New("file-store URL has to start with \"http(s)://\"") + } + + cacheDir := filepath.Join(os.TempDir(), "abf_cache") + os.MkdirAll(cacheDir, 0755) + cache := diskcache.New(cacheDir) + transport := httpcache.NewTransport(cache) + + client := &http.Client{ + Transport: transport, + Timeout: 30 * time.Second, + } + + lpw := fmt.Sprintf("%s:%s", login, password) + encodedLpw := base64.StdEncoding.EncodeToString([]byte(lpw)) + + return &AbfJson{ + login: login, + password: password, + abfURL: strings.TrimSuffix(abfURL, "/"), + fileStoreURL: strings.TrimSuffix(fileStoreURL, "/"), + base64AuthStr: encodedLpw, + log: logger, + client: client, + }, nil +} + +func (a *AbfJson) bytesToHuman(n int64, format string, symbols string) string { + if n < 0 { + panic(fmt.Sprintf("n < 0 (%d)", n)) + } + + sym := symbols + if strings.HasSuffix(sym, "_long") { + sym = sym[:len(sym)-6] + } + + prefix := map[string]int64{ + "k": 1 << 10, + "m": 1 << 20, + "g": 1 << 30, + "t": 1 << 40, + "ki": 1 << 10, + "mi": 1 << 20, + "gi": 1 << 30, + "ti": 1 << 40, + } + + for _, suffix := range symbols { + if n >= prefix[string(suffix)] { + n /= prefix[string(suffix)] + } else { + return fmt.Sprintf(format, n, suffix) + } + } + + return fmt.Sprintf(format, n, sym) +} + +func (a *AbfJson) getURLContents(path string, params url.Values, method string, body io.Reader) ([]byte, error) { + var fullURL string + if strings.HasPrefix(path, "/api/v1/upload") { + fullURL = a.fileStoreURL + path + } else { + fullURL = a.abfURL + path + } + + if len(params) > 0 { + fullURL += "?" + params.Encode() + } + + req, err := http.NewRequest(method, fullURL, body) + if err != nil { + return nil, err + } + + req.Header.Set("Authorization", "Basic "+a.base64AuthStr) + if method == http.MethodPost || method == http.MethodPut || method == http.MethodDelete { + req.Header.Set("Content-Type", "application/json") + } + + resp, err := a.client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + if resp.StatusCode >= 400 { + return nil, fmt.Errorf("HTTP error %d: %s", resp.StatusCode, resp.Status) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + return data, nil +} + +func (a *AbfJson) processResponse(data []byte) (map[string]interface{}, error) { + var response map[string]interface{} + err := json.Unmarshal(data, &response) + if err != nil { + return nil, fmt.Errorf("internal server error: it has returned non-json data") + } + + message, ok := response["message"].(string) + if ok && message != "Errors during build publishing!" && message != "Build is queued for publishing" { + return nil, fmt.Errorf(message) + } + + if errData, ok := response["error"].(string); ok { + return nil, fmt.Errorf(errData) + } + + if errData, ok := response["Error"].(string); ok { + return nil, fmt.Errorf(errData) + } + + return response, nil +} + +func (a *AbfJson) GetArchitectures() (map[string]interface{}, error) { + data, err := a.getURLContents("/api/v1/arches.json", nil, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetPlatforms(typ string) (map[string]interface{}, error) { + params := url.Values{} + if typ != "" { + params["type"] = []string{typ} + } + data, err := a.getURLContents("/api/v1/platforms.json", params, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetPlatformByID(plID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/platforms/%d.json", plID) + data, err := a.getURLContents(path, nil, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetUserID(username string) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/users/%s.json", username) + data, err := a.getURLContents(path, nil, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetBuildPlatforms() (map[string]interface{}, error) { + data, err := a.getURLContents("/api/v1/platforms/platforms_for_build.json", nil, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetRepositoryByID(repID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/repositories/%d.json", repID) + data, err := a.getURLContents(path, nil, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetBuildListByID(blID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/build_lists/%d.json", blID) + data, err := a.getURLContents(path, nil, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetListBuildLists(prjID int, filterQuery url.Values, page int) (map[string]interface{}, error) { + filterQuery.Set("page", fmt.Sprintf("%d", page)) + filterQuery.Set("per_page", "10") + path := fmt.Sprintf("/api/v1/build_lists.json") + data, err := a.getURLContents(path, filterQuery, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetProjectBuildLists(prjID int, filterQuery url.Values, page int) (map[string]interface{}, error) { + filterQuery.Set("page", fmt.Sprintf("%d", page)) + filterQuery.Set("per_page", "10") + path := fmt.Sprintf("/api/v1/projects/%d/build_lists.json", prjID) + data, err := a.getURLContents(path, filterQuery, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetProjectByID(pID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/projects/%d.json", pID) + data, err := a.getURLContents(path, nil, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetProjectIDByName(key [2]string) (map[string]interface{}, error) { + params := url.Values{"name": {key[1]}, "owner": {key[0]}} + data, err := a.getURLContents("/api/v1/projects/get_id.json", params, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) NewBuildTask(data map[string]interface{}) (map[string]interface{}, error) { + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + dataBytes, err := a.getURLContents("/api/v1/build_lists.json", nil, http.MethodPost, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + + return a.processResponse(dataBytes) +} + +func (a *AbfJson) Publish(taskID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/build_lists/%d/publish.json", taskID) + jsonData, err := json.Marshal(map[string]interface{}{"nothing": 1}) + if err != nil { + return nil, err + } + + dataBytes, err := a.getURLContents(path, nil, http.MethodPut, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + + return a.processResponse(dataBytes) +} + +func (a *AbfJson) NewPullRequest(data map[string]interface{}, pID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/projects/%d/pull_requests.json", pID) + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + dataBytes, err := a.getURLContents(path, nil, http.MethodPost, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + + return a.processResponse(dataBytes) +} + +func (a *AbfJson) UpdateProject(data map[string]interface{}, pID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/projects/%d.json", pID) + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + dataBytes, err := a.getURLContents(path, nil, http.MethodPut, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + + return a.processResponse(dataBytes) +} + +func (a *AbfJson) RemoveProjectFromRepo(data map[string]interface{}, repoID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/repositories/%d/remove_project.json", repoID) + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + dataBytes, err := a.getURLContents(path, nil, http.MethodDelete, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + + return a.processResponse(dataBytes) +} + +func (a *AbfJson) ForkProject(data map[string]interface{}, projID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/projects/%d/fork.json", projID) + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + dataBytes, err := a.getURLContents(path, nil, http.MethodPost, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + + return a.processResponse(dataBytes) +} + +func (a *AbfJson) AliasProject(data map[string]interface{}, projID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/projects/%d/alias.json", projID) + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + dataBytes, err := a.getURLContents(path, nil, http.MethodPost, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + + return a.processResponse(dataBytes) +} + +func (a *AbfJson) DestroyProject(data map[string]interface{}, projID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/projects/%d.json", projID) + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + dataBytes, err := a.getURLContents(path, nil, http.MethodDelete, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + + return a.processResponse(dataBytes) +} + +func (a *AbfJson) AddProjectToRepo(data map[string]interface{}, repoID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/repositories/%d/add_project.json", repoID) + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + dataBytes, err := a.getURLContents(path, nil, http.MethodPut, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + + return a.processResponse(dataBytes) +} + +func (a *AbfJson) NewProject(data map[string]interface{}) (map[string]interface{}, error) { + jsonData, err := json.Marshal(data) + if err != nil { + return nil, err + } + + dataBytes, err := a.getURLContents("/api/v1/projects.json", nil, http.MethodPost, bytes.NewBuffer(jsonData)) + if err != nil { + return nil, err + } + + return a.processResponse(dataBytes) +} + +func (a *AbfJson) GetGitRefsList(projID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/projects/%d/refs_list.json", projID) + data, err := a.getURLContents(path, nil, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetUserByID(userID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/users/%d.json", userID) + data, err := a.getURLContents(path, nil, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetGroupByID(groupID int) (map[string]interface{}, error) { + path := fmt.Sprintf("/api/v1/groups/%d.json", groupID) + data, err := a.getURLContents(path, nil, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetSearchResults(searchType, query string) (map[string]interface{}, error) { + params := url.Values{"type": {searchType}, "query": {query}, "per_page": {"100"}} + data, err := a.getURLContents("/api/v1/search.json", params, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetList(listType string, page int) (map[string]interface{}, error) { + params := url.Values{"page": {fmt.Sprintf("%d", page)}, "per_page": {"100"}} + path := fmt.Sprintf("/api/v1/%s.json", listType) + data, err := a.getURLContents(path, params, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) GetProjectsSingle(repoID, page int) (map[string]interface{}, error) { + params := url.Values{"page": {fmt.Sprintf("%d", page)}, "per_page": {"100"}} + path := fmt.Sprintf("/api/v1/repositories/%d/projects.json", repoID) + data, err := a.getURLContents(path, params, http.MethodGet, nil) + if err != nil { + return nil, err + } + + return a.processResponse(data) +} + +func (a *AbfJson) ComputeSHA1(filePath string) (string, error) { + file, err := os.Open(filePath) + if err != nil { + return "", err + } + defer file.Close() + + hasher := sha1.New() + if _, err := io.Copy(hasher, file); err != nil { + return "", err + } + + return fmt.Sprintf("%x", hasher.Sum(nil)), nil +} + +func (a *AbfJson) UploadFile(filePath string, silent bool) (string, error) { + hash, err := a.ComputeSHA1(filePath) + if err != nil { + return "", err + } + + a.log.Printf("File hash is %s", hash) + + res, err := a.GetFileInfoByHash(hash) + if err != nil { + return "", err + } + + if len(res) > 0 && res[0]["sha1_hash"] == hash { + newFn := filepath.Base(filePath) + oldFn := res[0]["file_name"].(string) + if oldFn != newFn && !silent { + a.log.Printf("The name of the file in file-store is %s, but you are trying to upload file %s", oldFn, newFn) + } + return hash, nil + } + + body := &bytes.Buffer{} + writer := multipart.NewWriter(body) + + part, err := writer.CreateFormFile("file_store[file]", filePath) + if err != nil { + return "", err + } + + file, err := os.Open(filePath) + if err != nil { + return "", err + } + defer file.Close() + + if _, err := io.Copy(part, file); err != nil { + return "", err + } + + if err := writer.Close(); err != nil { + return "", err + } + + size := body.Len() + contentType := writer.FormDataContentType() + + if !silent { + a.log.Printf("Uploading %s (%s)", filePath, a.bytesToHuman(int64(size), "%d%s", "basic")) + } + + req, err := http.NewRequest(http.MethodPost, a.fileStoreURL+"/api/v1/upload", body) + if err != nil { + return "", err + } + + req.Header.Set("Content-Type", contentType) + req.Header.Set("Content-Length", fmt.Sprintf("%d", size)) + req.Header.Set("Authorization", "Basic "+a.base64AuthStr) + + resp, err := a.client.Do(req) + if err != nil { + return "", err + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode > 299 { + return "", fmt.Errorf("could not upload file. HTTP error %d %s", resp.StatusCode, resp.Status) + } + + var output map[string]interface{} + if err := json.NewDecoder(resp.Body).Decode(&output); err != nil { + return "", err + } + + shaHash, ok := output["sha1_hash"].(string) + if !ok { + return "", errors.New("invalid response from server") + } + + return shaHash, nil +} + +func (a *AbfJson) GetFileInfoByHash(shaHash string) ([]map[string]interface{}, error) { + params := url.Values{"hash": {shaHash}} + data, err := a.getURLContents("/api/v1/file_stores.json", params, http.MethodGet, nil) + if err != nil { + return nil, err + } + + var response []map[string]interface{} + if err := json.Unmarshal(data, &response); err != nil { + return nil, err + } + + return response, nil +} + +func (a *AbfJson) FetchFile(shaHash, path string) error { + URL := a.fileStoreURL + "/api/v1/file_stores/" + shaHash + resp, err := a.client.Get(URL) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("failed to fetch file: HTTP error %d %s", resp.StatusCode, resp.Status) + } + + out, err := os.Create(path) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, resp.Body) + return err +} \ No newline at end of file diff --git a/abfapi_test.go b/abfapi_test.go new file mode 100644 index 0000000..125bc6b --- /dev/null +++ b/abfapi_test.go @@ -0,0 +1,252 @@ +package abfapi + +import ( + "io/ioutil" + "log" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +// Создаем мок-сервер для тестирования +func setupMockServer(t *testing.T) (*httptest.Server, *AbfJson) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/api/v1/arches.json": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"architectures": ["x86_64", "arm64"]}`)) + case "/api/v1/platforms.json": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"platforms": [{"id": 1, "name": "Ubuntu"}, {"id": 2, "name": "CentOS"}]}`)) + case "/api/v1/platforms/1.json": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"platform": {"id": 1, "name": "Ubuntu"}}`)) + case "/api/v1/users/testuser.json": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"user": {"id": 1, "username": "testuser"}}`)) + case "/api/v1/build_lists.json": + if r.Method == http.MethodGet { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"build_lists": [{"id": 1, "name": "BuildList1"}]}`)) + } else if r.Method == http.MethodPost { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"build_list": {"id": 2, "name": "NewBuildList"}}`)) + } else { + w.WriteHeader(http.StatusMethodNotAllowed) + } + case "/api/v1/build_lists/1.json": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"build_list": {"id": 1, "name": "BuildList1"}}`)) + case "/api/v1/build_lists/1/publish.json": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"status": "published"}`)) + case "/api/v1/projects/1.json": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"project": {"id": 1, "name": "TestProject"}}`)) + case "/api/v1/projects/get_id.json": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"project": {"id": 1, "name": "TestProject"}}`)) + case "/api/v1/projects.json": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"projects": [{"id": 1, "name": "TestProject"}]}`)) + case "/api/v1/file_stores.json": + w.WriteHeader(http.StatusOK) + w.Write([]byte(`[{"sha1_hash": "1234567890abcdef1234567890abcdef12345678", "file_name": "testfile.txt"}]`)) + case "/api/v1/upload": + if r.Method == http.MethodPost { + w.WriteHeader(http.StatusOK) + w.Write([]byte(`{"sha1_hash": "1234567890abcdef1234567890abcdef12345678"}`)) + } else { + w.WriteHeader(http.StatusMethodNotAllowed) + } + case "/api/v1/file_stores/1234567890abcdef1234567890abcdef12345678": + w.WriteHeader(http.StatusOK) + w.Write([]byte("Hello, world!")) + default: + w.WriteHeader(http.StatusNotFound) + } + })) + + logger := log.New(ioutil.Discard, "", log.LstdFlags) + client, err := NewAbfJson(server.URL, server.URL, "testuser", "testpass", logger) + assert.NoError(t, err) + + return server, client +} + +func TestNewAbfJson(t *testing.T) { + logger := log.New(ioutil.Discard, "", log.LstdFlags) + client, err := NewAbfJson("http://example.com", "http://filestore.example.com", "testuser", "testpass", logger) + assert.NoError(t, err) + assert.NotNil(t, client) + assert.Equal(t, "http://example.com", client.abfURL) + assert.Equal(t, "http://filestore.example.com", client.fileStoreURL) + assert.Equal(t, "dGVzdHVzZXI6dGVzdHBhc3M=", client.base64AuthStr) +} + +func TestGetArchitectures(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + arches, err := client.GetArchitectures() + assert.NoError(t, err) + assert.NotNil(t, arches) + assert.Contains(t, arches, "architectures") + assert.IsType(t, []interface{}{}, arches["architectures"]) +} + +func TestGetPlatforms(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + platforms, err := client.GetPlatforms("") + assert.NoError(t, err) + assert.NotNil(t, platforms) + assert.Contains(t, platforms, "platforms") + assert.IsType(t, []interface{}{}, platforms["platforms"]) +} + +func TestGetPlatformByID(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + platform, err := client.GetPlatformByID(1) + assert.NoError(t, err) + assert.NotNil(t, platform) + assert.Contains(t, platform, "platform") + assert.IsType(t, map[string]interface{}{}, platform["platform"]) +} + +func TestGetUserID(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + user, err := client.GetUserID("testuser") + assert.NoError(t, err) + assert.NotNil(t, user) + assert.Contains(t, user, "user") + assert.IsType(t, map[string]interface{}{}, user["user"]) +} + +func TestGetBuildListByID(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + buildList, err := client.GetBuildListByID(1) + assert.NoError(t, err) + assert.NotNil(t, buildList) + assert.Contains(t, buildList, "build_list") + assert.IsType(t, map[string]interface{}{}, buildList["build_list"]) +} + +func TestPublish(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + result, err := client.Publish(1) + assert.NoError(t, err) + assert.NotNil(t, result) + assert.Contains(t, result, "status") + assert.Equal(t, "published", result["status"]) +} + +func TestGetProjectByID(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + project, err := client.GetProjectByID(1) + assert.NoError(t, err) + assert.NotNil(t, project) + assert.Contains(t, project, "project") + assert.IsType(t, map[string]interface{}{}, project["project"]) +} + +func TestGetProjectIDByName(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + project, err := client.GetProjectIDByName([2]string{"owner", "TestProject"}) + assert.NoError(t, err) + assert.NotNil(t, project) + assert.Contains(t, project, "project") + assert.IsType(t, map[string]interface{}{}, project["project"]) +} + +func TestNewProject(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + data := map[string]interface{}{ + "name": "NewProject", + "description": "This is a new project", + } + + project, err := client.NewProject(data) + assert.NoError(t, err) + assert.NotNil(t, project) + assert.Contains(t, project, "projects") + assert.IsType(t, []interface{}{}, project["projects"]) +} + +func TestUploadFile(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + // Создаем временный файл для загрузки + tmpfile, err := ioutil.TempFile("", "testfile") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpfile.Name()) + + // Записываем данные в файл + _, err = tmpfile.Write([]byte("Hello, world!")) + if err != nil { + t.Fatal(err) + } + if err := tmpfile.Close(); err != nil { + t.Fatal(err) + } + + hash, err := client.UploadFile(tmpfile.Name(), true) + assert.NoError(t, err) + assert.Equal(t, "1234567890abcdef1234567890abcdef12345678", hash) +} + +func TestGetFileInfoByHash(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + info, err := client.GetFileInfoByHash("1234567890abcdef1234567890abcdef12345678") + assert.NoError(t, err) + assert.NotNil(t, info) + assert.Len(t, info, 1) + assert.Contains(t, info[0], "sha1_hash") + assert.Equal(t, "1234567890abcdef1234567890abcdef12345678", info[0]["sha1_hash"]) +} + +func TestFetchFile(t *testing.T) { + server, client := setupMockServer(t) + defer server.Close() + + // Создаем временный файл для сохранения загруженных данных + tmpfile, err := ioutil.TempFile("", "downloadedfile") + if err != nil { + t.Fatal(err) + } + defer os.Remove(tmpfile.Name()) + defer tmpfile.Close() + + err = client.FetchFile("1234567890abcdef1234567890abcdef12345678", tmpfile.Name()) + assert.NoError(t, err) + + // Проверяем содержимое файла + content, err := ioutil.ReadFile(tmpfile.Name()) + if err != nil { + t.Fatal(err) + } + assert.Equal(t, "Hello, world!", string(content)) +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0c9a986 --- /dev/null +++ b/go.mod @@ -0,0 +1,16 @@ +module abfapi + +go 1.22.0 + +require ( + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 + github.com/stretchr/testify v1.8.1 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/peterbourgon/diskv v2.0.1+incompatible // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ec4505a --- /dev/null +++ b/go.sum @@ -0,0 +1,23 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= +github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=