mirror of
https://github.com/black-ant/Ant-Browser.git
synced 2026-07-14 18:48:55 +08:00
channel: master version: 1.1.0 source-ref: master published-at-utc: 2026-03-20T14:14:07Z
549 lines
17 KiB
Go
549 lines
17 KiB
Go
package launchcode_test
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/json"
|
||
"net/http"
|
||
"net/http/httptest"
|
||
"strings"
|
||
"testing"
|
||
|
||
"ant-chrome/backend/internal/browser"
|
||
"ant-chrome/backend/internal/launchcode"
|
||
)
|
||
|
||
func buildTestHandlerWithManager(svc *launchcode.LaunchCodeService, starter launchcode.BrowserStarter, mgr *browser.Manager) http.Handler {
|
||
srv := launchcode.NewLaunchServer(svc, starter, mgr, 0)
|
||
return launchcode.NewTestHandler(srv)
|
||
}
|
||
|
||
func newSelectorTestManager(profiles ...*browser.Profile) *browser.Manager {
|
||
items := make(map[string]*browser.Profile, len(profiles))
|
||
for _, profile := range profiles {
|
||
items[profile.ProfileId] = profile
|
||
}
|
||
return &browser.Manager{
|
||
Profiles: items,
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithKeywordSelector(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profile := &browser.Profile{
|
||
ProfileId: "profile-keyword",
|
||
ProfileName: "Amazon US",
|
||
GroupId: "group-sales",
|
||
Tags: []string{"电商", "北美"},
|
||
Keywords: []string{"amazon-us", "checkout", "buyer-account"},
|
||
Pid: 9527,
|
||
DebugPort: 9333,
|
||
}
|
||
starter.addProfile(profile)
|
||
manager := newSelectorTestManager(profile)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
payload := bytes.NewBufferString(`{
|
||
"selector": {
|
||
"keyword": "checkout",
|
||
"tags": ["电商"],
|
||
"groupId": "group-sales"
|
||
},
|
||
"skipDefaultStartUrls": true
|
||
}`)
|
||
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", payload)
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("期望 200,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != profile.ProfileId {
|
||
t.Fatalf("命中实例错误: got=%s want=%s", starter.lastProfile, profile.ProfileId)
|
||
}
|
||
|
||
var resp struct {
|
||
OK bool `json:"ok"`
|
||
ProfileID string `json:"profileId"`
|
||
LaunchCode string `json:"launchCode"`
|
||
}
|
||
if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
|
||
t.Fatalf("解析响应失败: %v", err)
|
||
}
|
||
if !resp.OK || resp.ProfileID != profile.ProfileId {
|
||
t.Fatalf("响应不正确: %+v", resp)
|
||
}
|
||
if strings.TrimSpace(resp.LaunchCode) == "" {
|
||
t.Fatalf("期望返回 resolved launchCode,实际为空: %+v", resp)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithTopLevelKeywordSelector(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profile := &browser.Profile{
|
||
ProfileId: "profile-top-level",
|
||
ProfileName: "Billing Ops",
|
||
Keywords: []string{"billing", "invoice"},
|
||
Pid: 1001,
|
||
DebugPort: 9444,
|
||
}
|
||
starter.addProfile(profile)
|
||
manager := newSelectorTestManager(profile)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"keyword":"billing"}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("期望 200,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != profile.ProfileId {
|
||
t.Fatalf("命中实例错误: got=%s want=%s", starter.lastProfile, profile.ProfileId)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithTopLevelKeyAliasSelector(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profile := &browser.Profile{
|
||
ProfileId: "profile-top-level-key",
|
||
ProfileName: "Buyer Account",
|
||
Keywords: []string{"buyer-001", "amazon"},
|
||
Pid: 1002,
|
||
DebugPort: 9445,
|
||
}
|
||
starter.addProfile(profile)
|
||
manager := newSelectorTestManager(profile)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"key":"buyer-001"}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("期望 200,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != profile.ProfileId {
|
||
t.Fatalf("命中实例错误: got=%s want=%s", starter.lastProfile, profile.ProfileId)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithTopLevelKeyPrefersExactKeywordMatch(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profileFuzzy := &browser.Profile{
|
||
ProfileId: "profile-key-fuzzy",
|
||
ProfileName: "Account A",
|
||
Keywords: []string{"buyer-001-old", "amazon"},
|
||
Pid: 1004,
|
||
DebugPort: 9447,
|
||
}
|
||
profileExact := &browser.Profile{
|
||
ProfileId: "profile-key-exact",
|
||
ProfileName: "Z Account",
|
||
Keywords: []string{"buyer-001", "amazon"},
|
||
Pid: 1005,
|
||
DebugPort: 9448,
|
||
}
|
||
starter.addProfile(profileFuzzy)
|
||
starter.addProfile(profileExact)
|
||
manager := newSelectorTestManager(profileFuzzy, profileExact)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"key":"buyer-001"}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("期望 200,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != profileExact.ProfileId {
|
||
t.Fatalf("key 应优先命中精确关键字实例: got=%s want=%s", starter.lastProfile, profileExact.ProfileId)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithNestedSelectorKeyPrefersExactKeywordMatch(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profileFuzzy := &browser.Profile{
|
||
ProfileId: "profile-selector-key-fuzzy",
|
||
ProfileName: "Account A",
|
||
Keywords: []string{"buyer-001-old", "amazon"},
|
||
Pid: 1006,
|
||
DebugPort: 9449,
|
||
}
|
||
profileExact := &browser.Profile{
|
||
ProfileId: "profile-selector-key-exact",
|
||
ProfileName: "Z Account",
|
||
Keywords: []string{"buyer-001", "amazon"},
|
||
Pid: 1007,
|
||
DebugPort: 9450,
|
||
}
|
||
starter.addProfile(profileFuzzy)
|
||
starter.addProfile(profileExact)
|
||
manager := newSelectorTestManager(profileFuzzy, profileExact)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"selector":{"key":"buyer-001"}}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("期望 200,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != profileExact.ProfileId {
|
||
t.Fatalf("selector.key 应优先命中精确关键字实例: got=%s want=%s", starter.lastProfile, profileExact.ProfileId)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithTopLevelCodeFallbackToKeyword(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profile := &browser.Profile{
|
||
ProfileId: "profile-top-level-code-fallback",
|
||
ProfileName: "Buyer Account 01",
|
||
Keywords: []string{"buyer-001", "amazon"},
|
||
Pid: 1003,
|
||
DebugPort: 9446,
|
||
}
|
||
starter.addProfile(profile)
|
||
manager := newSelectorTestManager(profile)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"code":"buyer-001"}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("期望 200,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != profile.ProfileId {
|
||
t.Fatalf("code 关键字兜底命中实例错误: got=%s want=%s", starter.lastProfile, profile.ProfileId)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithTopLevelCodeFallbackPrefersExactKeywordMatch(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profileFuzzy := &browser.Profile{
|
||
ProfileId: "profile-code-fuzzy",
|
||
ProfileName: "Account A",
|
||
Keywords: []string{"buyer-001-old", "amazon"},
|
||
Pid: 1008,
|
||
DebugPort: 9451,
|
||
}
|
||
profileExact := &browser.Profile{
|
||
ProfileId: "profile-code-exact",
|
||
ProfileName: "Z Account",
|
||
Keywords: []string{"buyer-001", "amazon"},
|
||
Pid: 1009,
|
||
DebugPort: 9452,
|
||
}
|
||
starter.addProfile(profileFuzzy)
|
||
starter.addProfile(profileExact)
|
||
manager := newSelectorTestManager(profileFuzzy, profileExact)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"code":"buyer-001"}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("期望 200,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != profileExact.ProfileId {
|
||
t.Fatalf("code 关键字兜底应优先命中精确关键字实例: got=%s want=%s", starter.lastProfile, profileExact.ProfileId)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithAmbiguousKeywordSelectorReturnsFirstByDefault(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profileA := &browser.Profile{
|
||
ProfileId: "profile-a",
|
||
ProfileName: "Account A",
|
||
Keywords: []string{"shop", "checkout"},
|
||
Pid: 1001,
|
||
DebugPort: 9441,
|
||
}
|
||
profileB := &browser.Profile{
|
||
ProfileId: "profile-b",
|
||
ProfileName: "Account B",
|
||
Keywords: []string{"shop", "refund"},
|
||
Pid: 1002,
|
||
DebugPort: 9442,
|
||
}
|
||
starter.addProfile(profileA)
|
||
starter.addProfile(profileB)
|
||
manager := newSelectorTestManager(profileA, profileB)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"selector":{"keyword":"shop"}}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("期望 200,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != profileA.ProfileId {
|
||
t.Fatalf("关键字多命中时应默认取排序后的第一个实例: got=%s want=%s", starter.lastProfile, profileA.ProfileId)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithTopLevelCodeFallbackReturnsFirstByDefault(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profileA := &browser.Profile{
|
||
ProfileId: "profile-a",
|
||
ProfileName: "Account A",
|
||
Keywords: []string{"shop", "checkout"},
|
||
Pid: 1001,
|
||
DebugPort: 9441,
|
||
}
|
||
profileB := &browser.Profile{
|
||
ProfileId: "profile-b",
|
||
ProfileName: "Account B",
|
||
Keywords: []string{"shop", "refund"},
|
||
Pid: 1002,
|
||
DebugPort: 9442,
|
||
}
|
||
starter.addProfile(profileA)
|
||
starter.addProfile(profileB)
|
||
manager := newSelectorTestManager(profileA, profileB)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"code":"shop"}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("期望 200,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != profileA.ProfileId {
|
||
t.Fatalf("code 关键字兜底多命中时应默认取排序后的第一个实例: got=%s want=%s", starter.lastProfile, profileA.ProfileId)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithAmbiguousKeywordSelectorAndExplicitUniqueReturnsConflict(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profileA := &browser.Profile{
|
||
ProfileId: "profile-a",
|
||
ProfileName: "Account A",
|
||
Keywords: []string{"shop", "checkout"},
|
||
Pid: 1001,
|
||
DebugPort: 9441,
|
||
}
|
||
profileB := &browser.Profile{
|
||
ProfileId: "profile-b",
|
||
ProfileName: "Account B",
|
||
Keywords: []string{"shop", "refund"},
|
||
Pid: 1002,
|
||
DebugPort: 9442,
|
||
}
|
||
starter.addProfile(profileA)
|
||
starter.addProfile(profileB)
|
||
manager := newSelectorTestManager(profileA, profileB)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"selector":{"keyword":"shop","matchMode":"unique"}}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusConflict {
|
||
t.Fatalf("期望 409,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != "" {
|
||
t.Fatalf("歧义场景不应启动实例: %s", starter.lastProfile)
|
||
}
|
||
if !strings.Contains(w.Body.String(), "matchMode=first") {
|
||
t.Fatalf("错误信息未提示 matchMode=first: %s", w.Body.String())
|
||
}
|
||
}
|
||
|
||
func TestGetLaunchByCodeDoesNotFallbackToKeyword(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profile := &browser.Profile{
|
||
ProfileId: "profile-get-code-only",
|
||
ProfileName: "Buyer Account 02",
|
||
Keywords: []string{"buyer-002"},
|
||
Pid: 1004,
|
||
DebugPort: 9447,
|
||
}
|
||
starter.addProfile(profile)
|
||
manager := newSelectorTestManager(profile)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodGet, "/api/launch/buyer-002", nil)
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusNotFound {
|
||
t.Fatalf("GET /api/launch/{code} 应保持纯 code 语义,期望 404,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != "" {
|
||
t.Fatalf("GET /api/launch/{code} 不应按关键字兜底启动实例: %s", starter.lastProfile)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithMatchModeFirst(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profileB := &browser.Profile{
|
||
ProfileId: "profile-b",
|
||
ProfileName: "B Account",
|
||
Keywords: []string{"shop"},
|
||
Pid: 2002,
|
||
DebugPort: 9552,
|
||
}
|
||
profileA := &browser.Profile{
|
||
ProfileId: "profile-a",
|
||
ProfileName: "A Account",
|
||
Keywords: []string{"shop"},
|
||
Pid: 2001,
|
||
DebugPort: 9551,
|
||
}
|
||
starter.addProfile(profileA)
|
||
starter.addProfile(profileB)
|
||
manager := newSelectorTestManager(profileB, profileA)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"selector":{"keyword":"shop","matchMode":"first"}}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("期望 200,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if starter.lastProfile != profileA.ProfileId {
|
||
t.Fatalf("matchMode=first 应命中排序后的第一个实例: got=%s want=%s", starter.lastProfile, profileA.ProfileId)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithMatchModeAllStartsAllMatchedProfiles(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profileA := &browser.Profile{
|
||
ProfileId: "profile-a",
|
||
ProfileName: "A Account",
|
||
Keywords: []string{"shop"},
|
||
Pid: 2001,
|
||
DebugPort: 9551,
|
||
}
|
||
profileB := &browser.Profile{
|
||
ProfileId: "profile-b",
|
||
ProfileName: "B Account",
|
||
Keywords: []string{"shop"},
|
||
Pid: 2002,
|
||
DebugPort: 9552,
|
||
}
|
||
starter.addProfile(profileA)
|
||
starter.addProfile(profileB)
|
||
manager := newSelectorTestManager(profileB, profileA)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"selector":{"keyword":"shop","matchMode":"all"}}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusOK {
|
||
t.Fatalf("期望 200,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if len(starter.started) != 2 {
|
||
t.Fatalf("matchMode=all 应启动 2 个实例: %+v", starter.started)
|
||
}
|
||
if starter.started[0] != profileA.ProfileId || starter.started[1] != profileB.ProfileId {
|
||
t.Fatalf("matchMode=all 应按稳定排序依次启动: got=%+v", starter.started)
|
||
}
|
||
|
||
var resp struct {
|
||
OK bool `json:"ok"`
|
||
Count int `json:"count"`
|
||
Items []struct {
|
||
ProfileID string `json:"profileId"`
|
||
IsActive bool `json:"isActive"`
|
||
} `json:"items"`
|
||
ActiveProfileID string `json:"activeProfileId"`
|
||
}
|
||
if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
|
||
t.Fatalf("解析响应失败: %v", err)
|
||
}
|
||
if !resp.OK || resp.Count != 2 || len(resp.Items) != 2 {
|
||
t.Fatalf("批量启动响应错误: %+v", resp)
|
||
}
|
||
if resp.ActiveProfileID != profileB.ProfileId {
|
||
t.Fatalf("activeProfileId 错误: got=%s want=%s", resp.ActiveProfileID, profileB.ProfileId)
|
||
}
|
||
if resp.Items[0].ProfileID != profileA.ProfileId || resp.Items[1].ProfileID != profileB.ProfileId {
|
||
t.Fatalf("items 顺序错误: %+v", resp.Items)
|
||
}
|
||
if resp.Items[0].IsActive || !resp.Items[1].IsActive {
|
||
t.Fatalf("isActive 标记错误: %+v", resp.Items)
|
||
}
|
||
}
|
||
|
||
func TestLaunchWithTopLevelCodeFallbackAndExplicitUniqueReturnsConflict(t *testing.T) {
|
||
svc := newInMemoryService()
|
||
starter := newMockStarterWithParams()
|
||
|
||
profileA := &browser.Profile{
|
||
ProfileId: "profile-a",
|
||
ProfileName: "Account A",
|
||
Keywords: []string{"shop", "checkout"},
|
||
Pid: 1001,
|
||
DebugPort: 9441,
|
||
}
|
||
profileB := &browser.Profile{
|
||
ProfileId: "profile-b",
|
||
ProfileName: "Account B",
|
||
Keywords: []string{"shop", "refund"},
|
||
Pid: 1002,
|
||
DebugPort: 9442,
|
||
}
|
||
starter.addProfile(profileA)
|
||
starter.addProfile(profileB)
|
||
manager := newSelectorTestManager(profileA, profileB)
|
||
|
||
handler := buildTestHandlerWithManager(svc, starter, manager)
|
||
req := httptest.NewRequest(http.MethodPost, "/api/launch", bytes.NewBufferString(`{"code":"shop","matchMode":"unique"}`))
|
||
req.Header.Set("Content-Type", "application/json")
|
||
w := httptest.NewRecorder()
|
||
handler.ServeHTTP(w, req)
|
||
|
||
if w.Code != http.StatusConflict {
|
||
t.Fatalf("期望 409,实际 %d,body=%s", w.Code, w.Body.String())
|
||
}
|
||
if len(starter.started) != 0 {
|
||
t.Fatalf("显式 unique 不应启动任何实例: %+v", starter.started)
|
||
}
|
||
}
|