1+ package test
2+
3+ import (
4+ "encoding/json"
5+ "errors"
6+ "fmt"
7+ "os"
8+ "path/filepath"
9+ "testing"
10+ "time"
11+
12+ yt "github.com/porjo/youtubeuploader"
13+ "golang.org/x/oauth2"
14+ )
15+
16+ func TestIsInvalidGrant (t * testing.T ) {
17+ tests := []struct {
18+ name string
19+ err error
20+ want bool
21+ }{
22+ {
23+ name : "nil error" ,
24+ err : nil ,
25+ want : false ,
26+ },
27+ {
28+ name : "unrelated error" ,
29+ err : errors .New ("connection refused" ),
30+ want : false ,
31+ },
32+ {
33+ name : "RetrieveError with invalid_grant" ,
34+ err : & oauth2.RetrieveError {ErrorCode : "invalid_grant" , ErrorDescription : "Token has been expired or revoked." },
35+ want : true ,
36+ },
37+ {
38+ name : "RetrieveError with different code" ,
39+ err : & oauth2.RetrieveError {ErrorCode : "invalid_client" },
40+ want : false ,
41+ },
42+ {
43+ name : "wrapped RetrieveError with invalid_grant" ,
44+ err : fmt .Errorf ("token refresh failed: %w" , & oauth2.RetrieveError {ErrorCode : "invalid_grant" }),
45+ want : true ,
46+ },
47+ {
48+ name : "error string containing invalid_grant" ,
49+ err : errors .New (`oauth2: "invalid_grant" "Bad Request"` ),
50+ want : true ,
51+ },
52+ }
53+
54+ for _ , tt := range tests {
55+ t .Run (tt .name , func (t * testing.T ) {
56+ got := yt .IsInvalidGrant (tt .err )
57+ if got != tt .want {
58+ t .Errorf ("IsInvalidGrant() = %v, want %v" , got , tt .want )
59+ }
60+ })
61+ }
62+ }
63+
64+ func TestCacheFile_RoundTrip (t * testing.T ) {
65+ tmpDir := t .TempDir ()
66+ path := filepath .Join (tmpDir , "token.json" )
67+ cf := yt .CacheFile (path )
68+
69+ token := & oauth2.Token {
70+ AccessToken : "test-access" ,
71+ TokenType : "Bearer" ,
72+ RefreshToken : "test-refresh" ,
73+ Expiry : time .Now ().Add (time .Hour ).Truncate (time .Second ),
74+ }
75+
76+ if err := cf .PutToken (token ); err != nil {
77+ t .Fatalf ("PutToken failed: %v" , err )
78+ }
79+
80+ got , err := cf .Token ()
81+ if err != nil {
82+ t .Fatalf ("Token failed: %v" , err )
83+ }
84+
85+ if got .AccessToken != token .AccessToken {
86+ t .Errorf ("AccessToken = %q, want %q" , got .AccessToken , token .AccessToken )
87+ }
88+ if got .RefreshToken != token .RefreshToken {
89+ t .Errorf ("RefreshToken = %q, want %q" , got .RefreshToken , token .RefreshToken )
90+ }
91+ }
92+
93+ func TestCacheFile_ReauthOnInvalidGrant (t * testing.T ) {
94+ tmpDir := t .TempDir ()
95+ tokenPath := filepath .Join (tmpDir , "request.token" )
96+
97+ expiredToken := & oauth2.Token {
98+ AccessToken : "expired-access" ,
99+ TokenType : "Bearer" ,
100+ RefreshToken : "revoked-refresh" ,
101+ Expiry : time .Now ().Add (- time .Hour ),
102+ }
103+ data , _ := json .Marshal (expiredToken )
104+ if err := os .WriteFile (tokenPath , data , 0600 ); err != nil {
105+ t .Fatalf ("failed to write token file: %v" , err )
106+ }
107+
108+ // Verify file exists and is readable
109+ cf := yt .CacheFile (tokenPath )
110+ _ , err := cf .Token ()
111+ if err != nil {
112+ t .Fatalf ("should be able to read token: %v" , err )
113+ }
114+
115+ // Simulate invalid_grant error from token refresh
116+ refreshErr := & oauth2.RetrieveError {ErrorCode : "invalid_grant" }
117+ if ! yt .IsInvalidGrant (refreshErr ) {
118+ t .Fatal ("expected IsInvalidGrant to return true for invalid_grant error" )
119+ }
120+
121+ // Verify file is NOT deleted; PutToken will overwrite it when new token is written
122+ if _ , err := os .Stat (tokenPath ); os .IsNotExist (err ) {
123+ t .Error ("token cache file should NOT be deleted on invalid_grant; re-auth will overwrite it" )
124+ }
125+ }
126+
127+ func TestCacheFile_TokenReturnsErrorForMissingFile (t * testing.T ) {
128+ cf := yt .CacheFile ("/nonexistent/path/token.json" )
129+ _ , err := cf .Token ()
130+ if err == nil {
131+ t .Error ("expected error for missing file, got nil" )
132+ }
133+ }
134+
135+ func TestCacheFile_PutTokenOverwritesExisting (t * testing.T ) {
136+ tmpDir := t .TempDir ()
137+ path := filepath .Join (tmpDir , "token.json" )
138+ cf := yt .CacheFile (path )
139+
140+ old := & oauth2.Token {AccessToken : "old-token" , TokenType : "Bearer" }
141+ if err := cf .PutToken (old ); err != nil {
142+ t .Fatalf ("PutToken (old) failed: %v" , err )
143+ }
144+
145+ updated := & oauth2.Token {AccessToken : "new-token" , TokenType : "Bearer" }
146+ if err := cf .PutToken (updated ); err != nil {
147+ t .Fatalf ("PutToken (new) failed: %v" , err )
148+ }
149+
150+ got , err := cf .Token ()
151+ if err != nil {
152+ t .Fatalf ("Token failed: %v" , err )
153+ }
154+ if got .AccessToken != "new-token" {
155+ t .Errorf ("AccessToken = %q, want %q" , got .AccessToken , "new-token" )
156+ }
157+ }
0 commit comments