-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
189 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
// Package statefile defines the state file | ||
package statefile | ||
|
||
import ( | ||
"errors" | ||
"sync" | ||
"time" | ||
|
||
"github.com/ooni/probe-engine/internal/orchestra/login" | ||
) | ||
|
||
// State is the state stored inside the state file | ||
type State struct { | ||
ClientID string | ||
Expire time.Time | ||
Password string | ||
Token string | ||
} | ||
|
||
// Auth returns an authentication structure, if possible, otherwise | ||
// it returns nil, meaning that you should login again. | ||
func (s State) Auth() *login.Auth { | ||
if s.Token == "" { | ||
return nil | ||
} | ||
if time.Now().Add(30 * time.Second).After(s.Expire) { | ||
return nil | ||
} | ||
return &login.Auth{ | ||
Expire: s.Expire, | ||
Token: s.Token, | ||
} | ||
} | ||
|
||
// Credentials returns login credentials, if possible, otherwise it | ||
// returns nil, meaning that you should create an account. | ||
func (s State) Credentials() *login.Credentials { | ||
if s.ClientID == "" { | ||
return nil | ||
} | ||
if s.Password == "" { | ||
return nil | ||
} | ||
return &login.Credentials{ | ||
ClientID: s.ClientID, | ||
Password: s.Password, | ||
} | ||
} | ||
|
||
// StateFile is a generic state file | ||
type StateFile interface { | ||
Set(*State) error | ||
Get() (*State, error) | ||
} | ||
|
||
type memory struct { | ||
state State | ||
mu sync.Mutex | ||
} | ||
|
||
// NewMemory creates a new state file in memory | ||
func NewMemory(workdir string) StateFile { | ||
return &memory{} | ||
} | ||
|
||
func (sf *memory) Set(s *State) error { | ||
if s == nil { | ||
return errors.New("passed nil pointer") | ||
} | ||
sf.mu.Lock() | ||
defer sf.mu.Unlock() | ||
sf.state = *s | ||
return nil | ||
} | ||
|
||
func (sf *memory) Get() (*State, error) { | ||
sf.mu.Lock() | ||
defer sf.mu.Unlock() | ||
state := sf.state | ||
return &state, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package statefile | ||
|
||
import ( | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestUnitStateAuth(t *testing.T) { | ||
t.Run("with no Token", func(t *testing.T) { | ||
state := State{Expire: time.Now().Add(10 * time.Hour)} | ||
if state.Auth() != nil { | ||
t.Fatal("expected nil here") | ||
} | ||
}) | ||
t.Run("with expired Token", func(t *testing.T) { | ||
state := State{ | ||
Expire: time.Now().Add(-1 * time.Hour), | ||
Token: "xx-x-xxx-xx", | ||
} | ||
if state.Auth() != nil { | ||
t.Fatal("expected nil here") | ||
} | ||
}) | ||
t.Run("with good Token", func(t *testing.T) { | ||
state := State{ | ||
Expire: time.Now().Add(10 * time.Hour), | ||
Token: "xx-x-xxx-xx", | ||
} | ||
if state.Auth() == nil { | ||
t.Fatal("expected valid auth here") | ||
} | ||
}) | ||
} | ||
|
||
func TestUnitStateCredentials(t *testing.T) { | ||
t.Run("with no ClientID", func(t *testing.T) { | ||
state := State{} | ||
if state.Credentials() != nil { | ||
t.Fatal("expected nil here") | ||
} | ||
}) | ||
t.Run("with no Password", func(t *testing.T) { | ||
state := State{ | ||
ClientID: "xx-x-xxx-xx", | ||
} | ||
if state.Credentials() != nil { | ||
t.Fatal("expected nil here") | ||
} | ||
}) | ||
t.Run("with all good", func(t *testing.T) { | ||
state := State{ | ||
ClientID: "xx-x-xxx-xx", | ||
Password: "xx", | ||
} | ||
if state.Credentials() == nil { | ||
t.Fatal("expected valid auth here") | ||
} | ||
}) | ||
} | ||
|
||
func TestUnitStateFileMemory(t *testing.T) { | ||
sf := NewMemory("/tmp") | ||
if sf == nil { | ||
t.Fatal("expected non nil pointer here") | ||
} | ||
if err := sf.Set(nil); err == nil { | ||
t.Fatal("expected an error here") | ||
} | ||
s := State{ | ||
Expire: time.Now(), | ||
Password: "xy", | ||
Token: "abc", | ||
ClientID: "xx", | ||
} | ||
if err := sf.Set(&s); err != nil { | ||
t.Fatal(err) | ||
} | ||
os, err := sf.Get() | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if s.ClientID != os.ClientID { | ||
t.Fatal("the ClientID field has changed") | ||
} | ||
if !s.Expire.Equal(os.Expire) { | ||
t.Fatal("the Expire field has changed") | ||
} | ||
if s.Password != os.Password { | ||
t.Fatal("the Password field has changed") | ||
} | ||
if s.Token != os.Token { | ||
t.Fatal("the Token field has changed") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters