// organizations_commands_test.go — #45 的驗收測試:tea organizations 對等指令 // list/create/delete。讀取 list 打對端點;寫入 create/delete 未帶 --yes // 不得發出任何請求,帶 --yes 時打對端點與 payload。 package cli import ( "fmt" "net/http" "strings" "testing" ) // TestOrganizationsListGetsUserOrgs 驗證 organizations list: // GET /user/orgs,輸出 name(username 優先)與 full_name 等欄位。 func TestOrganizationsListGetsUserOrgs(t *testing.T) { var gotMethod, gotPath string run := opsTestServer(t, func(w http.ResponseWriter, r *http.Request) { gotMethod, gotPath = r.Method, r.URL.Path fmt.Fprint(w, `[{"username":"alterminal","name":"alterminal","full_name":"Alterminal Co., Ltd.","website":"","location":"","description":"","visibility":"public"},{"username":"","name":"fallback-org","full_name":"Fallback Org","visibility":"public"}]`) }) stdout, _, code := run("organizations", "list") if code != 0 { t.Fatalf("organizations list 應回 0,得到 %d", code) } if gotMethod != "GET" || !strings.HasSuffix(gotPath, "/user/orgs") { t.Errorf("應 GET /user/orgs,得到 %s %s", gotMethod, gotPath) } if !strings.Contains(stdout, `"name":"alterminal"`) || !strings.Contains(stdout, `"full_name":"Alterminal Co., Ltd."`) { t.Errorf("輸出應含 name 與 full_name,得到:%s", stdout) } // username 缺漏時回退 name 欄位(同 workflow.Org.Account 取值順序)。 if !strings.Contains(stdout, `"name":"fallback-org"`) { t.Errorf("username 缺漏時 name 应回退 name 欄位,得到:%s", stdout) } } // TestOrganizationsListRejectsPositionalArgs 驗證 list 不接受位置參數(回 2)。 func TestOrganizationsListRejectsPositionalArgs(t *testing.T) { run := opsTestServer(t, func(w http.ResponseWriter, r *http.Request) { t.Errorf("不應發出任何請求") }) _, stderr, code := run("organizations", "list", "alterminal") if code != 2 { t.Errorf("list 帶位置參數應回 2,得到 %d", code) } if !strings.Contains(stderr, "位置參數") { t.Errorf("錯誤訊息應提及位置參數,得到:%s", stderr) } } // TestOrganizationsCreateRequiresYes 驗證 create 未帶 --yes:回 2 且不發請求。 func TestOrganizationsCreateRequiresYes(t *testing.T) { run := opsTestServer(t, func(w http.ResponseWriter, r *http.Request) { t.Errorf("未帶 --yes 不得發出請求(%s %s)", r.Method, r.URL.Path) }) _, stderr, code := run("organizations", "create", "--name", "neworg") if code != 2 { t.Errorf("create 未帶 --yes 應回 2,得到 %d", code) } if !strings.Contains(stderr, "--yes") { t.Errorf("錯誤訊息應提及 --yes,得到:%s", stderr) } } // TestOrganizationsCreateRequiresName 驗證 create 缺 --name:回 2(且在 --yes 閘門之前檢查)。 func TestOrganizationsCreateRequiresName(t *testing.T) { run := opsTestServer(t, func(w http.ResponseWriter, r *http.Request) { t.Errorf("缺 --name 不應發出請求") }) _, stderr, code := run("organizations", "create", "--yes") if code != 2 { t.Errorf("create 缺 --name 應回 2,得到 %d", code) } if !strings.Contains(stderr, "--name") { t.Errorf("錯誤訊息應提及 --name,得到:%s", stderr) } } // TestOrganizationsCreatePostsOrg 驗證 create --yes: // POST /orgs,payload 帶 username 與 full_name;visibility 空時不傳。 func TestOrganizationsCreatePostsOrg(t *testing.T) { var gotMethod, gotPath, gotBody string run := opsTestServer(t, func(w http.ResponseWriter, r *http.Request) { gotMethod, gotPath = r.Method, r.URL.Path buf := make([]byte, 1024) n, _ := r.Body.Read(buf) gotBody = string(buf[:n]) fmt.Fprint(w, `{"username":"neworg","name":"neworg","full_name":"New Org","website":"https://x","location":"TW","description":"d","visibility":"public"}`) }) stdout, _, code := run("organizations", "create", "--yes", "--name", "neworg", "--full-name", "New Org", "--website", "https://x", "--location", "TW", "--description", "d") if code != 0 { t.Fatalf("create --yes 應回 0,得到 %d", code) } if gotMethod != "POST" || !strings.HasSuffix(gotPath, "/orgs") { t.Errorf("應 POST /orgs,得到 %s %s", gotMethod, gotPath) } for _, want := range []string{`"username":"neworg"`, `"full_name":"New Org"`, `"website":"https://x"`, `"location":"TW"`, `"description":"d"`} { if !strings.Contains(gotBody, want) { t.Errorf("body 應含 %s,得到:%s", want, gotBody) } } if strings.Contains(gotBody, "visibility") { t.Errorf("未指定 --visibility 時 body 不應含 visibility,得到:%s", gotBody) } if !strings.Contains(stdout, `"name":"neworg"`) || !strings.Contains(stdout, `"full_name":"New Org"`) { t.Errorf("輸出應含 name 與 full_name,得到:%s", stdout) } } // TestOrganizationsCreateRejectsBadVisibility 驗證 --visibility 值域檢查(回 2,不發請求)。 func TestOrganizationsCreateRejectsBadVisibility(t *testing.T) { run := opsTestServer(t, func(w http.ResponseWriter, r *http.Request) { t.Errorf("值域檢查失敗不應發出請求") }) _, stderr, code := run("organizations", "create", "--yes", "--name", "neworg", "--visibility", "secret") if code != 2 { t.Errorf("--visibility secret 應回 2,得到 %d", code) } if !strings.Contains(stderr, "visibility") { t.Errorf("錯誤訊息應提及 visibility,得到:%s", stderr) } } // TestOrganizationsDeleteRequiresYes 驗證 delete 未帶 --yes:回 2 且不發請求。 func TestOrganizationsDeleteRequiresYes(t *testing.T) { run := opsTestServer(t, func(w http.ResponseWriter, r *http.Request) { t.Errorf("未帶 --yes 不得發出請求(%s %s)", r.Method, r.URL.Path) }) _, stderr, code := run("organizations", "delete", "oldorg") if code != 2 { t.Errorf("delete 未帶 --yes 應回 2,得到 %d", code) } if !strings.Contains(stderr, "--yes") { t.Errorf("錯誤訊息應提及 --yes,得到:%s", stderr) } } // TestOrganizationsDeleteOrg 驗證 delete --yes:DELETE /orgs/{org}。 func TestOrganizationsDeleteOrg(t *testing.T) { var gotMethod, gotPath string run := opsTestServer(t, func(w http.ResponseWriter, r *http.Request) { gotMethod, gotPath = r.Method, r.URL.Path w.WriteHeader(http.StatusNoContent) }) stdout, _, code := run("organizations", "delete", "--yes", "oldorg") if code != 0 { t.Fatalf("delete --yes 應回 0,得到 %d", code) } if gotMethod != "DELETE" || !strings.HasSuffix(gotPath, "/orgs/oldorg") { t.Errorf("應 DELETE /orgs/oldorg,得到 %s %s", gotMethod, gotPath) } if !strings.Contains(stdout, `"state":"deleted"`) { t.Errorf("輸出應含 state=deleted,得到:%s", stdout) } } // TestOrganizationsUnknownSubcommand 驗證未知子命令回 2。 func TestOrganizationsUnknownSubcommand(t *testing.T) { run := opsTestServer(t, func(w http.ResponseWriter, r *http.Request) { t.Errorf("未知子命令不應發出請求") }) _, stderr, code := run("organizations", "rename") if code != 2 { t.Errorf("未知子命令應回 2,得到 %d", code) } if !strings.Contains(stderr, "organizations") { t.Errorf("錯誤訊息應提及 organizations,得到:%s", stderr) } }