github.com/qiniu/go-sdk/v7@v7.27.0/sandbox/envd_test.go (about) 1 package sandbox 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/sha256" 7 "fmt" 8 "io" 9 "mime" 10 "mime/multipart" 11 "testing" 12 ) 13 14 func TestGetHost(t *testing.T) { 15 domain := "example.com" 16 sb := &Sandbox{sandboxID: "sb-123", domain: &domain} 17 18 got := sb.GetHost(8080) 19 want := "8080-sb-123.example.com" 20 if got != want { 21 t.Errorf("GetHost = %q, want %q", got, want) 22 } 23 } 24 25 func TestGetHostNilDomain(t *testing.T) { 26 sb := &Sandbox{sandboxID: "sb-456"} 27 28 got := sb.GetHost(3000) 29 if got != "" { 30 t.Errorf("GetHost with nil domain = %q, want empty string", got) 31 } 32 } 33 34 func TestGetHostEmptyDomain(t *testing.T) { 35 domain := "" 36 sb := &Sandbox{sandboxID: "sb-789", domain: &domain} 37 38 got := sb.GetHost(443) 39 if got != "" { 40 t.Errorf("GetHost with empty domain = %q, want empty string", got) 41 } 42 } 43 44 func TestEnvdURL(t *testing.T) { 45 domain := "test.dev" 46 sb := &Sandbox{sandboxID: "sb-100", domain: &domain, client: &Client{config: &Config{}}} 47 48 got := sb.envdURL() 49 want := "https://49983-sb-100.test.dev" 50 if got != want { 51 t.Errorf("envdURL = %q, want %q", got, want) 52 } 53 } 54 55 func TestEnvdBasicAuth(t *testing.T) { 56 auth := envdBasicAuth("testuser") 57 // base64("testuser:") = "dGVzdHVzZXI6" 58 want := "Basic dGVzdHVzZXI6" 59 if auth != want { 60 t.Errorf("envdBasicAuth = %q, want %q", auth, want) 61 } 62 } 63 64 func TestFileSignature(t *testing.T) { 65 sig := fileSignature("/test/file.txt", "read", "user", "token123", 300) 66 raw := "/test/file.txt:read:user:token123:300" 67 hash := sha256.Sum256([]byte(raw)) 68 want := "v1_" + fmt.Sprintf("%x", hash) 69 if sig != want { 70 t.Errorf("fileSignature = %q, want %q", sig, want) 71 } 72 } 73 74 func TestDownloadURL(t *testing.T) { 75 domain := "test.dev" 76 token := "mytoken" 77 sb := &Sandbox{sandboxID: "sb-100", domain: &domain, envdAccessToken: &token, client: &Client{config: &Config{}}} 78 79 u := sb.DownloadURL("/home/user/file.txt") 80 // 验证 URL 包含必要的查询参数 81 if u == "" { 82 t.Fatal("DownloadURL returned empty string") 83 } 84 // 检查基础结构 85 if got := "https://49983-sb-100.test.dev/files?"; len(u) < len(got) || u[:len(got)] != got { 86 t.Errorf("DownloadURL prefix = %q, want prefix %q", u, got) 87 } 88 } 89 90 func TestUploadURL(t *testing.T) { 91 domain := "test.dev" 92 token := "mytoken" 93 sb := &Sandbox{sandboxID: "sb-100", domain: &domain, envdAccessToken: &token, client: &Client{config: &Config{}}} 94 95 u := sb.UploadURL("/home/user/file.txt") 96 if u == "" { 97 t.Fatal("UploadURL returned empty string") 98 } 99 if got := "https://49983-sb-100.test.dev/files?"; len(u) < len(got) || u[:len(got)] != got { 100 t.Errorf("UploadURL prefix = %q, want prefix %q", u, got) 101 } 102 } 103 104 func TestDownloadURLWithoutToken(t *testing.T) { 105 domain := "test.dev" 106 sb := &Sandbox{sandboxID: "sb-100", domain: &domain, client: &Client{config: &Config{}}} 107 108 u := sb.DownloadURL("/file.txt") 109 // 没有 token 时不应包含 signature 参数 110 if u == "" { 111 t.Fatal("DownloadURL returned empty string") 112 } 113 } 114 115 func TestFilesLazyInit(t *testing.T) { 116 domain := "test.dev" 117 sb := &Sandbox{sandboxID: "sb-100", domain: &domain, client: &Client{config: &Config{}}} 118 119 fs1 := sb.Files() 120 fs2 := sb.Files() 121 if fs1 != fs2 { 122 t.Error("Files() should return the same instance") 123 } 124 } 125 126 func TestCommandsLazyInit(t *testing.T) { 127 domain := "test.dev" 128 sb := &Sandbox{sandboxID: "sb-100", domain: &domain, client: &Client{config: &Config{}}} 129 130 cmd1 := sb.Commands() 131 cmd2 := sb.Commands() 132 if cmd1 != cmd2 { 133 t.Error("Commands() should return the same instance") 134 } 135 } 136 137 func TestPtyLazyInit(t *testing.T) { 138 domain := "test.dev" 139 sb := &Sandbox{sandboxID: "sb-100", domain: &domain, client: &Client{config: &Config{}}} 140 141 pty1 := sb.Pty() 142 pty2 := sb.Pty() 143 if pty1 != pty2 { 144 t.Error("Pty() should return the same instance") 145 } 146 } 147 148 func TestFileURLOptionWithUser(t *testing.T) { 149 domain := "test.dev" 150 token := "tok" 151 sb := &Sandbox{sandboxID: "sb-1", domain: &domain, envdAccessToken: &token, client: &Client{config: &Config{}}} 152 153 u := sb.DownloadURL("/file.txt", WithFileUser("admin")) 154 // 验证 URL 包含 username=admin 155 if u == "" { 156 t.Fatal("DownloadURL returned empty string") 157 } 158 } 159 160 func TestIsNotFoundError(t *testing.T) { 161 apiErr := &APIError{StatusCode: 404, Body: []byte("not found")} 162 if !isNotFoundError(apiErr) { 163 t.Error("expected isNotFoundError to return true for 404 APIError") 164 } 165 166 apiErr200 := &APIError{StatusCode: 200, Body: []byte("ok")} 167 if isNotFoundError(apiErr200) { 168 t.Error("expected isNotFoundError to return false for 200 APIError") 169 } 170 } 171 172 func TestEntryInfoFromProtoNil(t *testing.T) { 173 if entryInfoFromProto(nil) != nil { 174 t.Error("entryInfoFromProto(nil) should return nil") 175 } 176 } 177 178 func TestWriteFilesEmpty(t *testing.T) { 179 domain := "test.dev" 180 sb := &Sandbox{sandboxID: "sb-100", domain: &domain, client: &Client{config: &Config{}}} 181 fs := &Filesystem{sandbox: sb} 182 183 infos, err := fs.WriteFiles(context.Background(), nil) 184 if err != nil { 185 t.Fatalf("WriteFiles(nil) 应返回 nil error,得到: %v", err) 186 } 187 if infos != nil { 188 t.Fatalf("WriteFiles(nil) 应返回 nil,得到: %v", infos) 189 } 190 } 191 192 func TestBatchUploadURL(t *testing.T) { 193 domain := "test.dev" 194 sb := &Sandbox{sandboxID: "sb-100", domain: &domain, client: &Client{config: &Config{}}} 195 196 u := sb.batchUploadURL("user") 197 want := "https://49983-sb-100.test.dev/files?username=user" 198 if u != want { 199 t.Errorf("batchUploadURL = %q, want %q", u, want) 200 } 201 } 202 203 func TestWriteFileFullPath(t *testing.T) { 204 var buf bytes.Buffer 205 w := newMultipartWriter(&buf) 206 207 if err := w.writeFileFullPath("file_0", "/home/user/test.txt", []byte("hello")); err != nil { 208 t.Fatalf("writeFileFullPath 失败: %v", err) 209 } 210 if err := w.close(); err != nil { 211 t.Fatalf("close 失败: %v", err) 212 } 213 214 // 解析 multipart 内容,验证 Content-Disposition 中的 filename 是完整路径。 215 // 注意:Part.FileName() 会调用 filepath.Base(),所以直接解析 header。 216 r := multipart.NewReader(&buf, w.w.Boundary()) 217 part, err := r.NextPart() 218 if err != nil { 219 t.Fatalf("NextPart 失败: %v", err) 220 } 221 _, params, err := mime.ParseMediaType(part.Header.Get("Content-Disposition")) 222 if err != nil { 223 t.Fatalf("ParseMediaType 失败: %v", err) 224 } 225 if got := params["filename"]; got != "/home/user/test.txt" { 226 t.Errorf("filename = %q, want %q", got, "/home/user/test.txt") 227 } 228 data, _ := io.ReadAll(part) 229 if string(data) != "hello" { 230 t.Errorf("data = %q, want %q", string(data), "hello") 231 } 232 } 233 234 func TestWriteFileBaseName(t *testing.T) { 235 var buf bytes.Buffer 236 w := newMultipartWriter(&buf) 237 238 if err := w.writeFile("file", "/home/user/test.txt", []byte("hello")); err != nil { 239 t.Fatalf("writeFile 失败: %v", err) 240 } 241 if err := w.close(); err != nil { 242 t.Fatalf("close 失败: %v", err) 243 } 244 245 // 解析 multipart 内容,验证 filename 是 basename 246 r := multipart.NewReader(&buf, w.w.Boundary()) 247 part, err := r.NextPart() 248 if err != nil { 249 t.Fatalf("NextPart 失败: %v", err) 250 } 251 if got := part.FileName(); got != "test.txt" { 252 t.Errorf("filename = %q, want %q", got, "test.txt") 253 } 254 }