@@ -5,176 +5,12 @@ package mail
55
66import (
77 "errors"
8- "io"
9- "net/http"
10- "net/http/httptest"
118 "strings"
129 "testing"
1310
14- "github.com/larksuite/cli/errs"
1511 "github.com/larksuite/cli/internal/output"
1612)
1713
18- func TestDownloadSignatureImageRejectsInvalidURLs (t * testing.T ) {
19- rt := newDownloadRuntime (t , & http.Client {})
20-
21- cases := []struct {
22- name string
23- url string
24- }{
25- {name : "invalid" , url : "https://[::1" },
26- {name : "http" , url : "http://example.com/sig.png" },
27- {name : "no host" , url : "https:///sig.png" },
28- }
29-
30- for _ , tc := range cases {
31- t .Run (tc .name , func (t * testing.T ) {
32- _ , _ , err := downloadSignatureImage (rt , tc .url , "sig.png" )
33- var internalErr * errs.InternalError
34- if ! errors .As (err , & internalErr ) {
35- t .Fatalf ("expected internal error, got %T (%v)" , err , err )
36- }
37- p , ok := errs .ProblemOf (err )
38- if ! ok {
39- t .Fatalf ("expected typed problem, got %T" , err )
40- }
41- if p .Subtype != errs .SubtypeInvalidResponse {
42- t .Fatalf ("subtype = %q, want %q" , p .Subtype , errs .SubtypeInvalidResponse )
43- }
44- })
45- }
46- }
47-
48- func TestDownloadSignatureImageHTTPErrorClassification (t * testing.T ) {
49- for _ , tc := range []struct {
50- name string
51- statusCode int
52- wantType any
53- wantSub errs.Subtype
54- retryable bool
55- }{
56- {
57- name : "server" ,
58- statusCode : http .StatusInternalServerError ,
59- wantType : (* errs .NetworkError )(nil ),
60- wantSub : errs .SubtypeNetworkServer ,
61- retryable : true ,
62- },
63- {
64- name : "not found" ,
65- statusCode : http .StatusNotFound ,
66- wantType : (* errs .APIError )(nil ),
67- wantSub : errs .SubtypeNotFound ,
68- },
69- } {
70- t .Run (tc .name , func (t * testing.T ) {
71- srv := httptest .NewTLSServer (http .HandlerFunc (func (w http.ResponseWriter , _ * http.Request ) {
72- http .Error (w , "download failed" , tc .statusCode )
73- }))
74- t .Cleanup (srv .Close )
75- rt := newDownloadRuntime (t , srv .Client ())
76-
77- _ , _ , err := downloadSignatureImage (rt , srv .URL + "/sig.png" , "sig.png" )
78- switch tc .wantType .(type ) {
79- case * errs.NetworkError :
80- var networkErr * errs.NetworkError
81- if ! errors .As (err , & networkErr ) {
82- t .Fatalf ("expected network error, got %T (%v)" , err , err )
83- }
84- case * errs.APIError :
85- var apiErr * errs.APIError
86- if ! errors .As (err , & apiErr ) {
87- t .Fatalf ("expected API error, got %T (%v)" , err , err )
88- }
89- }
90- p , ok := errs .ProblemOf (err )
91- if ! ok {
92- t .Fatalf ("expected typed problem, got %T" , err )
93- }
94- if p .Code != tc .statusCode {
95- t .Fatalf ("code = %d, want %d" , p .Code , tc .statusCode )
96- }
97- if p .Subtype != tc .wantSub {
98- t .Fatalf ("subtype = %q, want %q" , p .Subtype , tc .wantSub )
99- }
100- if p .Retryable != tc .retryable {
101- t .Fatalf ("retryable = %v, want %v" , p .Retryable , tc .retryable )
102- }
103- })
104- }
105- }
106-
107- func TestDownloadSignatureImageReadAndSizeErrors (t * testing.T ) {
108- readErr := errors .New ("socket closed" )
109- rt := newDownloadRuntime (t , & http.Client {
110- Transport : signatureRoundTripper (func (req * http.Request ) (* http.Response , error ) {
111- return & http.Response {
112- StatusCode : http .StatusOK ,
113- Header : make (http.Header ),
114- Body : signatureErrorBody {err : readErr },
115- Request : req ,
116- }, nil
117- }),
118- })
119-
120- _ , _ , err := downloadSignatureImage (rt , "https://example.com/sig.png" , "sig.png" )
121- var networkErr * errs.NetworkError
122- if ! errors .As (err , & networkErr ) {
123- t .Fatalf ("expected network error, got %T (%v)" , err , err )
124- }
125- if ! errors .Is (err , readErr ) {
126- t .Fatalf ("read cause not preserved: %v" , err )
127- }
128-
129- rt = newDownloadRuntime (t , & http.Client {
130- Transport : signatureRoundTripper (func (req * http.Request ) (* http.Response , error ) {
131- return & http.Response {
132- StatusCode : http .StatusOK ,
133- Header : make (http.Header ),
134- Body : & bodyFileTestFile {remaining : 10 * 1024 * 1024 + 1 },
135- Request : req ,
136- }, nil
137- }),
138- })
139-
140- _ , _ , err = downloadSignatureImage (rt , "https://example.com/huge.png" , "huge.png" )
141- var validationErr * errs.ValidationError
142- if ! errors .As (err , & validationErr ) {
143- t .Fatalf ("expected validation error, got %T (%v)" , err , err )
144- }
145- p , ok := errs .ProblemOf (err )
146- if ! ok {
147- t .Fatalf ("expected typed problem, got %T" , err )
148- }
149- if p .Subtype != errs .SubtypeFailedPrecondition {
150- t .Fatalf ("subtype = %q, want %q" , p .Subtype , errs .SubtypeFailedPrecondition )
151- }
152- }
153-
154- func TestDownloadSignatureImageSuccessUsesFilenameContentType (t * testing.T ) {
155- rt := newDownloadRuntime (t , & http.Client {
156- Transport : signatureRoundTripper (func (req * http.Request ) (* http.Response , error ) {
157- return & http.Response {
158- StatusCode : http .StatusOK ,
159- Header : make (http.Header ),
160- Body : io .NopCloser (strings .NewReader ("gif-data" )),
161- Request : req ,
162- }, nil
163- }),
164- })
165-
166- data , contentType , err := downloadSignatureImage (rt , "https://example.com/sig.gif" , "sig.gif" )
167- if err != nil {
168- t .Fatalf ("downloadSignatureImage failed: %v" , err )
169- }
170- if string (data ) != "gif-data" {
171- t .Fatalf ("data = %q" , string (data ))
172- }
173- if contentType != "image/gif" {
174- t .Fatalf ("content type = %q, want image/gif" , contentType )
175- }
176- }
177-
17814func TestValidateNoSignatureConflictTypedError (t * testing.T ) {
17915 err := validateNoSignatureConflict (true , "sig_123" )
18016 if err == nil {
@@ -183,13 +19,16 @@ func TestValidateNoSignatureConflictTypedError(t *testing.T) {
18319 // output.ErrValidation returns *output.ExitError with exit code ExitValidation (2).
18420 var exitErr * output.ExitError
18521 if ! errors .As (err , & exitErr ) {
186- t .Fatalf ("expected *output.ExitError, got %T (%v) " , err , err )
22+ t .Fatalf ("expected *output.ExitError, got %T: %v " , err , err )
18723 }
18824 if exitErr .Code != output .ExitValidation {
189- t .Fatalf ("exit code = %d, want %d (ExitValidation)" , exitErr .Code , output .ExitValidation )
25+ t .Errorf ("expected exit code %d (ExitValidation), got %d" , output .ExitValidation , exitErr .Code )
26+ }
27+ if exitErr .Detail == nil || exitErr .Detail .Type != "validation" {
28+ t .Errorf ("expected detail type validation, got %+v" , exitErr .Detail )
19029 }
19130 if ! strings .Contains (err .Error (), "mutually exclusive" ) {
192- t .Fatalf ("error message = %q, want it to contain \" mutually exclusive\" " , err .Error ())
31+ t .Errorf ("error message = %q, want it to contain \" mutually exclusive\" " , err .Error ())
19332 }
19433}
19534
@@ -214,7 +53,7 @@ func TestInjectPlainTextSignatureEmptyHTML(t *testing.T) {
21453 sig := & signatureResult {RenderedContent : " <br> " }
21554 body := "Hello world"
21655 got := injectPlainTextSignature (body , sig )
217- // PlainTextFromHTML on whitespace-only HTML collapses to empty → no change
56+ // PlainTextFromHTML on whitespace-only HTML collapses to empty — body unchanged.
21857 if got != body {
21958 t .Fatalf ("expected unchanged body for empty HTML sig, got %q" , got )
22059 }
@@ -233,30 +72,15 @@ func TestInjectPlainTextSignatureAppendsWithBlankLine(t *testing.T) {
23372}
23473
23574func TestInjectPlainTextSignatureTrimsTrailingNewlines (t * testing.T ) {
236- // RenderedContent whose plain-text rendering ends in newlines — they must be trimmed.
75+ // RenderedContent whose plain-text rendering ends in newlines must be trimmed.
23776 sig := & signatureResult {RenderedContent : "<p>Alice</p>" }
23877 body := "My message"
23978 got := injectPlainTextSignature (body , sig )
24079 // Result must not end with a bare newline after the signature text.
24180 if strings .HasSuffix (got , "\n " ) {
24281 t .Fatalf ("result should not end with newline, got %q" , got )
24382 }
244- }
245-
246- type signatureRoundTripper func (* http.Request ) (* http.Response , error )
247-
248- func (rt signatureRoundTripper ) RoundTrip (req * http.Request ) (* http.Response , error ) {
249- return rt (req )
250- }
251-
252- type signatureErrorBody struct {
253- err error
254- }
255-
256- func (b signatureErrorBody ) Read ([]byte ) (int , error ) {
257- return 0 , b .err
258- }
259-
260- func (b signatureErrorBody ) Close () error {
261- return nil
83+ if ! strings .Contains (got , "Alice" ) {
84+ t .Fatalf ("expected sig text in result, got %q" , got )
85+ }
26286}
0 commit comments