@@ -1723,7 +1723,14 @@ func TestResolveAcceptsValidAuthTypes(t *testing.T) {
17231723 Profiles : map [string ]config.Profile {
17241724 "default" : {
17251725 BaseURL : "https://example.com" ,
1726- Auth : config.AuthConfig {Type : "basic" , Username : "u" , Token : "t" },
1726+ Auth : config.AuthConfig {
1727+ Type : "basic" ,
1728+ Username : "u" ,
1729+ Token : "t" ,
1730+ ClientID : "cid" ,
1731+ ClientSecret : "csecret" ,
1732+ TokenURL : "https://auth.example.com/token" ,
1733+ },
17271734 },
17281735 },
17291736 DefaultProfile : "default" ,
@@ -2070,3 +2077,161 @@ func TestOAuth2Failure_SingleError_ExitAuth(t *testing.T) {
20702077 t .Errorf ("expected auth_error in stderr, got: %s" , lines [0 ])
20712078 }
20722079}
2080+
2081+ // --- Bug #53: OAuth2 auth type via env var should validate required fields ---
2082+
2083+ func TestResolveOAuth2MissingFields (t * testing.T ) {
2084+ dir := t .TempDir ()
2085+ cfgPath := dir + "/config.json"
2086+ cfg := & config.Config {
2087+ Profiles : map [string ]config.Profile {
2088+ "default" : {
2089+ BaseURL : "https://example.com" ,
2090+ Auth : config.AuthConfig {Type : "basic" , Username : "u" , Token : "t" },
2091+ },
2092+ },
2093+ DefaultProfile : "default" ,
2094+ }
2095+ if err := config .SaveTo (cfg , cfgPath ); err != nil {
2096+ t .Fatalf ("SaveTo: %v" , err )
2097+ }
2098+
2099+ // Override auth type to oauth2 via flags (simulating env var or --auth-type).
2100+ flags := & config.FlagOverrides {AuthType : "oauth2" }
2101+ _ , err := config .Resolve (cfgPath , "" , flags )
2102+ if err == nil {
2103+ t .Fatal ("expected error for oauth2 without required fields, got nil" )
2104+ }
2105+ if ! strings .Contains (err .Error (), "client_id" ) {
2106+ t .Errorf ("error should mention client_id, got: %v" , err )
2107+ }
2108+ if ! strings .Contains (err .Error (), "client_secret" ) {
2109+ t .Errorf ("error should mention client_secret, got: %v" , err )
2110+ }
2111+ if ! strings .Contains (err .Error (), "token_url" ) {
2112+ t .Errorf ("error should mention token_url, got: %v" , err )
2113+ }
2114+ }
2115+
2116+ func TestResolveOAuth2WithAllFieldsSucceeds (t * testing.T ) {
2117+ dir := t .TempDir ()
2118+ cfgPath := dir + "/config.json"
2119+ cfg := & config.Config {
2120+ Profiles : map [string ]config.Profile {
2121+ "default" : {
2122+ BaseURL : "https://example.com" ,
2123+ Auth : config.AuthConfig {
2124+ Type : "oauth2" ,
2125+ ClientID : "my-client" ,
2126+ ClientSecret : "my-secret" ,
2127+ TokenURL : "https://auth.example.com/token" ,
2128+ },
2129+ },
2130+ },
2131+ DefaultProfile : "default" ,
2132+ }
2133+ if err := config .SaveTo (cfg , cfgPath ); err != nil {
2134+ t .Fatalf ("SaveTo: %v" , err )
2135+ }
2136+
2137+ resolved , err := config .Resolve (cfgPath , "" , nil )
2138+ if err != nil {
2139+ t .Fatalf ("unexpected error: %v" , err )
2140+ }
2141+ if resolved .Auth .Type != "oauth2" {
2142+ t .Errorf ("Auth.Type = %q, want oauth2" , resolved .Auth .Type )
2143+ }
2144+ if resolved .Auth .ClientID != "my-client" {
2145+ t .Errorf ("Auth.ClientID = %q, want my-client" , resolved .Auth .ClientID )
2146+ }
2147+ }
2148+
2149+ func TestResolveOAuth2PartialFieldsMissing (t * testing.T ) {
2150+ dir := t .TempDir ()
2151+ cfgPath := dir + "/config.json"
2152+ cfg := & config.Config {
2153+ Profiles : map [string ]config.Profile {
2154+ "default" : {
2155+ BaseURL : "https://example.com" ,
2156+ Auth : config.AuthConfig {
2157+ Type : "oauth2" ,
2158+ ClientID : "my-client" ,
2159+ // Missing client_secret and token_url.
2160+ },
2161+ },
2162+ },
2163+ DefaultProfile : "default" ,
2164+ }
2165+ if err := config .SaveTo (cfg , cfgPath ); err != nil {
2166+ t .Fatalf ("SaveTo: %v" , err )
2167+ }
2168+
2169+ _ , err := config .Resolve (cfgPath , "" , nil )
2170+ if err == nil {
2171+ t .Fatal ("expected error for oauth2 with missing client_secret and token_url" )
2172+ }
2173+ if ! strings .Contains (err .Error (), "client_secret" ) {
2174+ t .Errorf ("error should mention client_secret, got: %v" , err )
2175+ }
2176+ if ! strings .Contains (err .Error (), "token_url" ) {
2177+ t .Errorf ("error should mention token_url, got: %v" , err )
2178+ }
2179+ // Should NOT mention client_id since it IS provided.
2180+ if strings .Contains (err .Error (), "client_id" ) {
2181+ t .Errorf ("error should NOT mention client_id (it is provided), got: %v" , err )
2182+ }
2183+ }
2184+
2185+ // --- Bug #54: Batch verbose log detection should use JSON parsing ---
2186+
2187+ func TestBatchVerboseLogDetection_ErrorWithTypeField (t * testing.T ) {
2188+ // Verify that an error message containing "type":"request" is NOT
2189+ // incorrectly forwarded as a verbose log line.
2190+ srv := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
2191+ w .WriteHeader (http .StatusBadRequest )
2192+ // Error body that contains the text "type":"request" to try to trick
2193+ // the verbose log detection heuristic.
2194+ w .Write ([]byte (`{"errorMessages":["invalid \"type\":\"request\" field"]}` ))
2195+ }))
2196+ defer srv .Close ()
2197+
2198+ var stdout , stderr bytes.Buffer
2199+ c := & client.Client {
2200+ BaseURL : srv .URL ,
2201+ Auth : config.AuthConfig {Type : "basic" , Username : "u" , Token : "t" },
2202+ HTTPClient : & http.Client {},
2203+ Stdout : & stdout ,
2204+ Stderr : & stderr ,
2205+ Paginate : true ,
2206+ Verbose : true ,
2207+ }
2208+
2209+ ctx := client .NewContext (context .Background (), c )
2210+ cmd := & cobra.Command {Use : "test" }
2211+ cmd .SetContext (ctx )
2212+
2213+ allOps := generated .AllSchemaOps ()
2214+ allOps = append (allOps , HandWrittenSchemaOps ()... )
2215+ opMap := make (map [string ]generated.SchemaOp , len (allOps ))
2216+ for _ , op := range allOps {
2217+ opMap [op .Resource + " " + op .Verb ] = op
2218+ }
2219+
2220+ bop := BatchOp {
2221+ Command : "issue get" ,
2222+ Args : map [string ]string {"issueIdOrKey" : "TEST-1" },
2223+ }
2224+ result := executeBatchOp (cmd , c , 0 , bop , opMap )
2225+
2226+ if result .ExitCode == 0 {
2227+ t .Error ("expected non-zero exit code for 400 error" )
2228+ }
2229+ // The error result should contain the error, not have it swallowed by verbose detection.
2230+ if result .Error == nil {
2231+ t .Error ("expected error in batch result, got nil" )
2232+ }
2233+ errStr := string (result .Error )
2234+ if ! strings .Contains (errStr , "errorMessages" ) && ! strings .Contains (errStr , "validation_error" ) && ! strings .Contains (errStr , "client_error" ) {
2235+ t .Errorf ("error result should contain the API error, got: %s" , errStr )
2236+ }
2237+ }
0 commit comments