Clean api package (#1055)

This commit is contained in:
Adrià Cidre 2018-06-27 10:11:45 +02:00 committed by GitHub
parent 3ca120c2aa
commit afb3ce159e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 75 deletions

View File

@ -5,30 +5,30 @@ import (
"strings" "strings"
) )
// AppState represents if the app is in foreground, background or some other state // appState represents if the app is in foreground, background or some other state
type AppState string type appState string
func (a AppState) String() string { func (a appState) String() string {
return string(a) return string(a)
} }
// Specific app states // Specific app states
// see https://facebook.github.io/react-native/docs/appstate.html // see https://facebook.github.io/react-native/docs/appstate.html
const ( const (
AppStateForeground = AppState("active") // these constant values are kept in sync with React Native appStateForeground = appState("active") // these constant values are kept in sync with React Native
AppStateBackground = AppState("background") appStateBackground = appState("background")
AppStateInactive = AppState("inactive") appStateInactive = appState("inactive")
AppStateInvalid = AppState("") appStateInvalid = appState("")
) )
// validAppStates returns an immutable set of valid states. // validAppStates returns an immutable set of valid states.
func validAppStates() []AppState { func validAppStates() []appState {
return []AppState{AppStateInactive, AppStateBackground, AppStateForeground} return []appState{appStateInactive, appStateBackground, appStateForeground}
} }
// ParseAppState creates AppState from a string // parseAppState creates AppState from a string
func ParseAppState(stateString string) (AppState, error) { func parseAppState(stateString string) (appState, error) {
// a bit of cleaning up // a bit of cleaning up
stateString = strings.ToLower(strings.TrimSpace(stateString)) stateString = strings.ToLower(strings.TrimSpace(stateString))
@ -38,5 +38,5 @@ func ParseAppState(stateString string) (AppState, error) {
} }
} }
return AppStateInvalid, fmt.Errorf("could not parse app state: %s", stateString) return appStateInvalid, fmt.Errorf("could not parse app state: %s", stateString)
} }

View File

@ -7,22 +7,22 @@ import (
) )
func TestParseAppType(t *testing.T) { func TestParseAppType(t *testing.T) {
check := func(input string, expectedState AppState, expectError bool) { check := func(input string, expectedState appState, expectError bool) {
actualState, err := ParseAppState(input) actualState, err := parseAppState(input)
assert.Equalf(t, expectedState, actualState, "unexpected result from ParseAppState") assert.Equalf(t, expectedState, actualState, "unexpected result from parseAppState")
if expectError { if expectError {
assert.NotNil(t, err, "error should not be nil") assert.NotNil(t, err, "error should not be nil")
} }
} }
check("active", AppStateForeground, false) check("active", appStateForeground, false)
check("background", AppStateBackground, false) check("background", appStateBackground, false)
check("inactive", AppStateInactive, false) check("inactive", appStateInactive, false)
check(" acTIVE ", AppStateForeground, false) check(" acTIVE ", appStateForeground, false)
check(" backGROUND ", AppStateBackground, false) check(" backGROUND ", appStateBackground, false)
check(" INACTIVE ", AppStateInactive, false) check(" INACTIVE ", appStateInactive, false)
check("", AppStateInvalid, true) check("", appStateInvalid, true)
check("back ground", AppStateInvalid, true) check("back ground", appStateInvalid, true)
check(" back ground ", AppStateInvalid, true) check(" back ground ", appStateInvalid, true)
check(" ", AppStateInvalid, true) check(" ", appStateInvalid, true)
} }

View File

@ -47,7 +47,8 @@ type StatusBackend struct {
transactor *transactions.Transactor transactor *transactions.Transactor
jailManager jail.Manager jailManager jail.Manager
newNotification fcm.NotificationConstructor newNotification fcm.NotificationConstructor
connectionState ConnectionState connectionState connectionState
appState appState
log log.Logger log log.Logger
} }
@ -90,11 +91,6 @@ func (b *StatusBackend) JailManager() jail.Manager {
return b.jailManager return b.jailManager
} }
// PersonalAPI returns reference to personal APIs
func (b *StatusBackend) PersonalAPI() *personal.PublicAPI {
return b.personalAPI
}
// Transactor returns reference to a status transactor // Transactor returns reference to a status transactor
func (b *StatusBackend) Transactor() *transactions.Transactor { func (b *StatusBackend) Transactor() *transactions.Transactor {
return b.transactor return b.transactor
@ -155,7 +151,7 @@ func (b *StatusBackend) startNode(config *params.NodeConfig) (err error) {
} }
b.log.Info("Handlers registered") b.log.Info("Handlers registered")
if err = b.ReSelectAccount(); err != nil { if err = b.reSelectAccount(); err != nil {
b.log.Error("Reselect account failed", "err", err) b.log.Error("Reselect account failed", "err", err)
return return
} }
@ -316,24 +312,22 @@ func (b *StatusBackend) registerHandlers() error {
return hash.Hex(), err return hash.Hex(), err
}) })
rpcClient.RegisterHandler(params.PersonalSignMethodName, b.PersonalAPI().Sign) rpcClient.RegisterHandler(params.PersonalSignMethodName, b.personalAPI.Sign)
rpcClient.RegisterHandler(params.PersonalRecoverMethodName, b.PersonalAPI().Recover) rpcClient.RegisterHandler(params.PersonalRecoverMethodName, b.personalAPI.Recover)
return nil return nil
} }
//
// ConnectionChange handles network state changes logic. // ConnectionChange handles network state changes logic.
func (b *StatusBackend) ConnectionChange(typ string, expensive bool) { func (b *StatusBackend) ConnectionChange(typ string, expensive bool) {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
state := ConnectionState{ state := connectionState{
Type: NewConnectionType(typ), Type: newConnectionType(typ),
Expensive: expensive, Expensive: expensive,
} }
if typ == "none" { if typ == none {
state.Offline = true state.Offline = true
} }
@ -348,13 +342,14 @@ func (b *StatusBackend) ConnectionChange(typ string, expensive bool) {
// AppStateChange handles app state changes (background/foreground). // AppStateChange handles app state changes (background/foreground).
// state values: see https://facebook.github.io/react-native/docs/appstate.html // state values: see https://facebook.github.io/react-native/docs/appstate.html
func (b *StatusBackend) AppStateChange(state string) { func (b *StatusBackend) AppStateChange(state string) {
appState, err := ParseAppState(state) s, err := parseAppState(state)
if err != nil { if err != nil {
log.Error("AppStateChange failed, ignoring", "error", err) log.Error("AppStateChange failed, ignoring", "error", err)
return // and do nothing return // and do nothing
} }
b.log.Info("App State changed", "new-state", appState) b.log.Info("App State changed", "new-state", s)
b.appState = s
// TODO: put node in low-power mode if the app is in background (or inactive) // TODO: put node in low-power mode if the app is in background (or inactive)
// and normal mode if the app is in foreground. // and normal mode if the app is in foreground.
@ -385,8 +380,8 @@ func (b *StatusBackend) Logout() error {
return nil return nil
} }
// ReSelectAccount selects previously selected account, often, after node restart. // reSelectAccount selects previously selected account, often, after node restart.
func (b *StatusBackend) ReSelectAccount() error { func (b *StatusBackend) reSelectAccount() error {
selectedAccount, err := b.AccountManager().SelectedAccount() selectedAccount, err := b.AccountManager().SelectedAccount()
if selectedAccount == nil || err == account.ErrNoAccountSelected { if selectedAccount == nil || err == account.ErrNoAccountSelected {
return nil return nil

View File

@ -103,7 +103,7 @@ func TestBackendGettersConcurrently(t *testing.T) {
wg.Add(1) wg.Add(1)
go func() { go func() {
assert.NotNil(t, backend.PersonalAPI()) assert.NotNil(t, backend.personalAPI)
wg.Done() wg.Done()
}() }()
@ -174,7 +174,7 @@ func TestBackendAccountsConcurrently(t *testing.T) {
wg.Add(1) wg.Add(1)
go func() { go func() {
assert.NoError(t, backend.ReSelectAccount()) assert.NoError(t, backend.reSelectAccount())
wg.Done() wg.Done()
}() }()
@ -189,7 +189,7 @@ func TestBackendAccountsConcurrently(t *testing.T) {
} }
func TestBackendConnectionChangesConcurrently(t *testing.T) { func TestBackendConnectionChangesConcurrently(t *testing.T) {
connections := [...]string{"wifi", "cellular"} connections := [...]string{wifi, cellular, unknown}
backend := NewStatusBackend() backend := NewStatusBackend()
count := 3 count := 3
@ -207,6 +207,18 @@ func TestBackendConnectionChangesConcurrently(t *testing.T) {
wg.Wait() wg.Wait()
} }
func TestBackendConnectionChangesToOffline(t *testing.T) {
b := NewStatusBackend()
b.ConnectionChange(none, false)
assert.True(t, b.connectionState.Offline)
b.ConnectionChange(wifi, false)
assert.False(t, b.connectionState.Offline)
b.ConnectionChange("unknown-state", false)
assert.False(t, b.connectionState.Offline)
}
func TestBackendCallRPCConcurrently(t *testing.T) { func TestBackendCallRPCConcurrently(t *testing.T) {
backend := NewStatusBackend() backend := NewStatusBackend()
config := params.NodeConfig{} config := params.NodeConfig{}
@ -245,6 +257,38 @@ func TestBackendCallRPCConcurrently(t *testing.T) {
wg.Wait() wg.Wait()
} }
func TestAppStateChange(t *testing.T) {
backend := NewStatusBackend()
var testCases = []struct {
name string
fromState appState
toState appState
expectedState appState
}{
{
name: "success",
fromState: appStateInactive,
toState: appStateBackground,
expectedState: appStateBackground,
},
{
name: "invalid state",
fromState: appStateInactive,
toState: "unexisting",
expectedState: appStateInactive,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
backend.appState = tc.fromState
backend.AppStateChange(tc.toState.String())
assert.Equal(t, tc.expectedState.String(), backend.appState.String())
})
}
}
func TestPrepareTxArgs(t *testing.T) { func TestPrepareTxArgs(t *testing.T) {
var flagtests = []struct { var flagtests = []struct {
description string description string

View File

@ -4,60 +4,61 @@ import (
"fmt" "fmt"
) )
// ConnectionState represents device connection state and type, // connectionState represents device connection state and type,
// as reported by mobile framework. // as reported by mobile framework.
// //
// Zero value represents default assumption about network (online and unknown type). // Zero value represents default assumption about network (online and unknown type).
type ConnectionState struct { type connectionState struct {
Offline bool `json:"offline"` Offline bool `json:"offline"`
Type ConnectionType `json:"type"` Type connectionType `json:"type"`
Expensive bool `json:"expensive"` Expensive bool `json:"expensive"`
} }
// ConnectionType represents description of available // connectionType represents description of available
// connection types as reported by React Native (see // connection types as reported by React Native (see
// https://facebook.github.io/react-native/docs/netinfo.html) // https://facebook.github.io/react-native/docs/netinfo.html)
// We're interested mainly in 'wifi' and 'cellular', but // We're interested mainly in 'wifi' and 'cellular', but
// other types are also may be used. // other types are also may be used.
type ConnectionType byte type connectionType byte
const ( const (
offline = "offline" offline = "offline"
wifi = "wifi" wifi = "wifi"
cellular = "cellular" cellular = "cellular"
unknown = "unknown" unknown = "unknown"
none = "none"
) )
// NewConnectionType creates new ConnectionType from string. // newConnectionType creates new connectionType from string.
func NewConnectionType(s string) ConnectionType { func newConnectionType(s string) connectionType {
switch s { switch s {
case cellular: case cellular:
return ConnectionCellular return connectionCellular
case wifi: case wifi:
return ConnectionWifi return connectionWifi
} }
return ConnectionUnknown return connectionUnknown
} }
// ConnectionType constants // ConnectionType constants
const ( const (
ConnectionUnknown ConnectionType = iota connectionUnknown connectionType = iota
ConnectionCellular // cellular, LTE, 4G, 3G, EDGE, etc. connectionCellular // cellular, LTE, 4G, 3G, EDGE, etc.
ConnectionWifi // WIFI or iOS simulator connectionWifi // WIFI or iOS simulator
) )
// String formats ConnectionState for logs. Implements Stringer. // string formats ConnectionState for logs. Implements Stringer.
func (c ConnectionState) String() string { func (c connectionState) String() string {
if c.Offline { if c.Offline {
return offline return offline
} }
var typ string var typ string
switch c.Type { switch c.Type {
case ConnectionWifi: case connectionWifi:
typ = wifi typ = wifi
case ConnectionCellular: case connectionCellular:
typ = cellular typ = cellular
default: default:
typ = unknown typ = unknown

View File

@ -3,16 +3,16 @@ package api
import "testing" import "testing"
func TestConnectionType(t *testing.T) { func TestConnectionType(t *testing.T) {
c := NewConnectionType("wifi") c := newConnectionType("wifi")
if c != ConnectionWifi { if c != connectionWifi {
t.Fatalf("Wrong connection type: %v", c) t.Fatalf("Wrong connection type: %v", c)
} }
c = NewConnectionType("cellular") c = newConnectionType("cellular")
if c != ConnectionCellular { if c != connectionCellular {
t.Fatalf("Wrong connection type: %v", c) t.Fatalf("Wrong connection type: %v", c)
} }
c = NewConnectionType("bluetooth") c = newConnectionType("bluetooth")
if c != ConnectionUnknown { if c != connectionUnknown {
t.Fatalf("Wrong connection type: %v", c) t.Fatalf("Wrong connection type: %v", c)
} }
} }
@ -20,34 +20,39 @@ func TestConnectionType(t *testing.T) {
func TestConnectionState(t *testing.T) { func TestConnectionState(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
state ConnectionState state connectionState
expected string expected string
}{ }{
{ {
"zero value", "zero value",
ConnectionState{}, connectionState{},
"unknown", "unknown",
}, },
{ {
"offline", "offline",
ConnectionState{Offline: true}, connectionState{Offline: true},
"offline", "offline",
}, },
{ {
"wifi", "wifi",
ConnectionState{Type: ConnectionWifi}, connectionState{Type: connectionWifi},
"wifi", "wifi",
}, },
{ {
"wifi tethered", "wifi tethered",
ConnectionState{Type: ConnectionWifi, Expensive: true}, connectionState{Type: connectionWifi, Expensive: true},
"wifi (expensive)", "wifi (expensive)",
}, },
{ {
"unknown", "unknown",
ConnectionState{Type: ConnectionUnknown}, connectionState{Type: connectionUnknown},
"unknown", "unknown",
}, },
{
"cellular",
connectionState{Type: connectionCellular},
"cellular",
},
} }
for _, test := range tests { for _, test := range tests {

View File

@ -1,5 +1,6 @@
package api package api
// RunAsync runs the specified function asynchronously.
func RunAsync(f func() error) <-chan error { func RunAsync(f func() error) <-chan error {
resp := make(chan error, 1) resp := make(chan error, 1)
go func() { go func() {