feat: add onboarding defaults
parent
1573ff6c24
commit
3552af6386
|
@ -98,6 +98,11 @@ func (c *Client) initialize(ctx context.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Always create Onboarding bucket.
|
||||
if err := c.initializeOnboarding(ctx, tx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Always create Source bucket.
|
||||
if err := c.initializeSources(ctx, tx); err != nil {
|
||||
return err
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
package bolt
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
bolt "github.com/coreos/bbolt"
|
||||
"github.com/influxdata/platform"
|
||||
)
|
||||
|
||||
var onboardingBucket = []byte("onboardingv1")
|
||||
var onboardingKey = []byte("onboarding_key")
|
||||
|
||||
var _ platform.OnboardingService = (*Client)(nil)
|
||||
|
||||
func (c *Client) initializeOnboarding(ctx context.Context, tx *bolt.Tx) error {
|
||||
if _, err := tx.CreateBucketIfNotExists([]byte(onboardingBucket)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsOnboarding checks onboardingBucket
|
||||
// to see if the onboarding key is true.
|
||||
func (c *Client) IsOnboarding(ctx context.Context) (isOnboarding bool, err error) {
|
||||
err = c.db.View(func(tx *bolt.Tx) error {
|
||||
result := tx.Bucket(onboardingBucket).Get(onboardingKey)
|
||||
isOnboarding = len(result) == 0
|
||||
return nil
|
||||
})
|
||||
return isOnboarding, err
|
||||
}
|
||||
|
||||
// PutOnboardingStatus will update the flag,
|
||||
// so future onboarding request will be denied.
|
||||
func (c *Client) PutOnboardingStatus(ctx context.Context, v bool) error {
|
||||
if v {
|
||||
return c.db.Update(func(tx *bolt.Tx) error {
|
||||
return tx.Bucket(onboardingBucket).Put(onboardingKey, []byte{0x1})
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Generate OnboardingResults from onboarding request,
|
||||
// update db so this request will be disabled for the second run.
|
||||
func (c *Client) Generate(ctx context.Context, req *platform.OnboardingRequest) (*platform.OnboardingResults, error) {
|
||||
isOnboarding, err := c.IsOnboarding(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !isOnboarding {
|
||||
return nil, &platform.Error{
|
||||
Code: platform.EConflict,
|
||||
Msg: "onboarding has already been completed",
|
||||
}
|
||||
}
|
||||
|
||||
if req.Password == "" {
|
||||
return nil, &platform.Error{
|
||||
Code: platform.EEmptyValue,
|
||||
Msg: "password is empty",
|
||||
}
|
||||
}
|
||||
|
||||
if req.User == "" {
|
||||
return nil, &platform.Error{
|
||||
Code: platform.EEmptyValue,
|
||||
Msg: "username is empty",
|
||||
}
|
||||
}
|
||||
|
||||
if req.Org == "" {
|
||||
return nil, &platform.Error{
|
||||
Code: platform.EEmptyValue,
|
||||
Msg: "org name is empty",
|
||||
}
|
||||
}
|
||||
|
||||
if req.Bucket == "" {
|
||||
return nil, &platform.Error{
|
||||
Code: platform.EEmptyValue,
|
||||
Msg: "bucket name is empty",
|
||||
}
|
||||
}
|
||||
|
||||
u := &platform.User{Name: req.User}
|
||||
if err := c.CreateUser(ctx, u); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = c.SetPassword(ctx, u.Name, req.Password); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
o := &platform.Organization{
|
||||
Name: req.Org,
|
||||
}
|
||||
if err = c.CreateOrganization(ctx, o); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bucket := &platform.Bucket{
|
||||
Name: req.Bucket,
|
||||
Organization: o.Name,
|
||||
OrganizationID: o.ID,
|
||||
}
|
||||
if err = c.CreateBucket(ctx, bucket); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
auth := &platform.Authorization{
|
||||
User: u.Name,
|
||||
UserID: u.ID,
|
||||
Permissions: []platform.Permission{
|
||||
platform.CreateUserPermission,
|
||||
platform.DeleteUserPermission,
|
||||
platform.Permission{
|
||||
Resource: platform.OrganizationResource,
|
||||
Action: platform.WriteAction,
|
||||
},
|
||||
platform.WriteBucketPermission(bucket.ID),
|
||||
},
|
||||
}
|
||||
if err = c.CreateAuthorization(ctx, auth); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = c.PutOnboardingStatus(ctx, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &platform.OnboardingResults{
|
||||
User: u,
|
||||
Org: o,
|
||||
Bucket: bucket,
|
||||
Auth: auth,
|
||||
}, nil
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package bolt_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
platformtesting "github.com/influxdata/platform/testing"
|
||||
)
|
||||
|
||||
func initOnboardingService(f platformtesting.OnboardingFields, t *testing.T) (platformtesting.OnBoardingNBasicAuthService, func()) {
|
||||
c, closeFn, err := NewTestClient()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new bolt client: %v", err)
|
||||
}
|
||||
c.IDGenerator = f.IDGenerator
|
||||
c.TokenGenerator = f.TokenGenerator
|
||||
ctx := context.TODO()
|
||||
if err = c.PutOnboardingStatus(ctx, !f.IsOnboarding); err != nil {
|
||||
t.Fatalf("failed to set new onboarding finished: %v", err)
|
||||
}
|
||||
|
||||
return c, func() {
|
||||
defer closeFn()
|
||||
if err := c.PutOnboardingStatus(ctx, false); err != nil {
|
||||
t.Logf("failed to remove onboarding finished: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGenerate(t *testing.T) {
|
||||
platformtesting.Generate(initOnboardingService, t)
|
||||
}
|
|
@ -184,6 +184,8 @@ func platformF(cmd *cobra.Command, args []string) {
|
|||
macroSvc = c
|
||||
}
|
||||
|
||||
var onboardingSvc platform.OnboardingService = c
|
||||
|
||||
var queryService query.QueryService
|
||||
{
|
||||
// TODO(lh): this is temporary until query endpoint is added here.
|
||||
|
@ -303,6 +305,9 @@ func platformF(cmd *cobra.Command, args []string) {
|
|||
sourceHandler.NewBucketService = source.NewBucketService
|
||||
sourceHandler.NewQueryService = source.NewQueryService
|
||||
|
||||
setupHandler := http.NewSetupHandler()
|
||||
setupHandler.OnboardingService = onboardingSvc
|
||||
|
||||
taskHandler := http.NewTaskHandler(logger)
|
||||
taskHandler.TaskService = taskSvc
|
||||
|
||||
|
@ -340,6 +345,7 @@ func platformF(cmd *cobra.Command, args []string) {
|
|||
MacroHandler: macroHandler,
|
||||
QueryHandler: queryHandler,
|
||||
WriteHandler: writeHandler,
|
||||
SetupHandler: setupHandler,
|
||||
}
|
||||
reg.MustRegister(platformHandler.PrometheusCollectors()...)
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ type Error struct {
|
|||
}
|
||||
|
||||
// Error implement the error interface by outputing the Code and Err.
|
||||
func (e Error) Error() string {
|
||||
func (e *Error) Error() string {
|
||||
var b strings.Builder
|
||||
|
||||
// Print the current operation in our stack, if any.
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/influxdata/platform"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
)
|
||||
|
||||
// SetupHandler represents an HTTP API handler for onboarding setup.
|
||||
type SetupHandler struct {
|
||||
*httprouter.Router
|
||||
|
||||
OnboardingService platform.OnboardingService
|
||||
}
|
||||
|
||||
// NewSetupHandler returns a new instance of SetupHandler.
|
||||
func NewSetupHandler() *SetupHandler {
|
||||
h := &SetupHandler{
|
||||
Router: httprouter.New(),
|
||||
}
|
||||
h.HandlerFunc("POST", "/setup", h.handlePostSetup)
|
||||
h.HandlerFunc("GET", "/setup", h.isOnboarding)
|
||||
return h
|
||||
}
|
||||
|
||||
// isOnboarding is the HTTP handler for the GET /setup route.
|
||||
// returns true/false
|
||||
func (h *SetupHandler) isOnboarding(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
result, err := h.OnboardingService.IsOnboarding(ctx)
|
||||
if err != nil {
|
||||
EncodeError(ctx, err, w)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, `{"allowed": %v}`, result)
|
||||
}
|
||||
|
||||
func (h *SetupHandler) handlePostSetup(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
req, err := decodePostSetupRequest(ctx, r)
|
||||
if err != nil {
|
||||
EncodeError(ctx, err, w)
|
||||
return
|
||||
}
|
||||
results, err := h.OnboardingService.Generate(ctx, req)
|
||||
if err != nil {
|
||||
EncodeError(ctx, err, w)
|
||||
return
|
||||
}
|
||||
if err := encodeResponse(ctx, w, http.StatusCreated, newOnboardingResponse(results)); err != nil {
|
||||
EncodeError(ctx, err, w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
type onboardingResponse struct {
|
||||
User *userResponse `json:"user"`
|
||||
Bucket *bucketResponse `json:"bucket"`
|
||||
Organization *orgResponse `json:"org"`
|
||||
Auth *authResponse `json:"auth"`
|
||||
}
|
||||
|
||||
func newOnboardingResponse(results *platform.OnboardingResults) *onboardingResponse {
|
||||
return &onboardingResponse{
|
||||
User: newUserResponse(results.User),
|
||||
Bucket: newBucketResponse(results.Bucket),
|
||||
Organization: newOrgResponse(results.Org),
|
||||
Auth: newAuthResponse(results.Auth),
|
||||
}
|
||||
}
|
||||
|
||||
func decodePostSetupRequest(ctx context.Context, r *http.Request) (*platform.OnboardingRequest, error) {
|
||||
req := &platform.OnboardingRequest{}
|
||||
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return req, nil
|
||||
}
|
|
@ -25,6 +25,7 @@ type PlatformHandler struct {
|
|||
FluxLangHandler *FluxLangHandler
|
||||
QueryHandler *FluxHandler
|
||||
WriteHandler *WriteHandler
|
||||
SetupHandler *SetupHandler
|
||||
}
|
||||
|
||||
func setCORSResponseHeaders(w nethttp.ResponseWriter, r *nethttp.Request) {
|
||||
|
@ -36,6 +37,7 @@ func setCORSResponseHeaders(w nethttp.ResponseWriter, r *nethttp.Request) {
|
|||
}
|
||||
|
||||
var platformLinks = map[string]interface{}{
|
||||
"setup": "/setup",
|
||||
"sources": "/v2/sources",
|
||||
"dashboards": "/v2/dashboards",
|
||||
"query": "/v2/query",
|
||||
|
@ -79,7 +81,8 @@ func (h *PlatformHandler) ServeHTTP(w nethttp.ResponseWriter, r *nethttp.Request
|
|||
// of the platform API.
|
||||
if !strings.HasPrefix(r.URL.Path, "/v1") &&
|
||||
!strings.HasPrefix(r.URL.Path, "/v2") &&
|
||||
!strings.HasPrefix(r.URL.Path, "/chronograf/") {
|
||||
!strings.HasPrefix(r.URL.Path, "/chronograf/") &&
|
||||
!strings.HasPrefix(r.URL.Path, "/setup") {
|
||||
h.AssetHandler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
@ -90,6 +93,11 @@ func (h *PlatformHandler) ServeHTTP(w nethttp.ResponseWriter, r *nethttp.Request
|
|||
return
|
||||
}
|
||||
|
||||
if strings.HasPrefix(r.URL.Path, "/setup") {
|
||||
h.SetupHandler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
var err error
|
||||
if ctx, err = extractAuthorization(ctx, r); err != nil {
|
||||
|
|
|
@ -15,6 +15,38 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Routes"
|
||||
/setup:
|
||||
get:
|
||||
tags:
|
||||
- Setup
|
||||
summary: check if database has default user, org, bucket created, returns true if not.
|
||||
response:
|
||||
'200':
|
||||
content:
|
||||
application/json:
|
||||
type: object
|
||||
properties:
|
||||
allowed:
|
||||
type: boolean
|
||||
post:
|
||||
tags:
|
||||
- Setup
|
||||
summary: post onboarding request, to setup initial user, org and bucket
|
||||
requestBody:
|
||||
description: source to create
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/OnboardingRequest"
|
||||
responses:
|
||||
'201':
|
||||
description: Created default user, bucket, org
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/OnboardingResponse"
|
||||
|
||||
/macros:
|
||||
get:
|
||||
tags:
|
||||
|
@ -3230,6 +3262,28 @@ components:
|
|||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Source"
|
||||
OnboardingRequest:
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
org:
|
||||
type: string
|
||||
bucket:
|
||||
type: string
|
||||
OnboardingResponse:
|
||||
type: object
|
||||
properties:
|
||||
user:
|
||||
$ref: "#/components/schemas/User"
|
||||
org:
|
||||
$ref: "#/components/schemas/Organization"
|
||||
bucket:
|
||||
$ref: "#/components/schemas/Bucket"
|
||||
auth:
|
||||
$ref: "#/components/schemas/Authorization"
|
||||
Health:
|
||||
type: object
|
||||
properties:
|
||||
|
@ -3245,4 +3299,4 @@ components:
|
|||
type: string
|
||||
enum:
|
||||
- unhealthy
|
||||
- healthy
|
||||
- healthy
|
|
@ -0,0 +1,28 @@
|
|||
package platform
|
||||
|
||||
import "context"
|
||||
|
||||
// OnboardingResults is a group of elements required for first run.
|
||||
type OnboardingResults struct {
|
||||
User *User `json:"user"`
|
||||
Org *Organization `json:"org"`
|
||||
Bucket *Bucket `json:"bucket"`
|
||||
Auth *Authorization `json:"auth"`
|
||||
}
|
||||
|
||||
// OnboardingRequest is the request
|
||||
// to setup defaults.
|
||||
type OnboardingRequest struct {
|
||||
User string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Org string `json:"org"`
|
||||
Bucket string `json:"bucket"`
|
||||
}
|
||||
|
||||
// OnboardingService represents a service for the first run.
|
||||
type OnboardingService interface {
|
||||
// IsOnboarding determine if onboarding request is allowed.
|
||||
IsOnboarding(ctx context.Context) (bool, error)
|
||||
// Generate OnboardingResults.
|
||||
Generate(ctx context.Context, req *OnboardingRequest) (*OnboardingResults, error)
|
||||
}
|
|
@ -0,0 +1,244 @@
|
|||
package testing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/influxdata/platform"
|
||||
"github.com/influxdata/platform/mock"
|
||||
)
|
||||
|
||||
// OnboardingFields will include the IDGenerator, TokenGenerator
|
||||
// and IsOnboarding
|
||||
type OnboardingFields struct {
|
||||
IDGenerator platform.IDGenerator
|
||||
TokenGenerator platform.TokenGenerator
|
||||
IsOnboarding bool
|
||||
}
|
||||
|
||||
// OnBoardingNBasicAuthService includes onboarding service
|
||||
// and basic auth service.
|
||||
type OnBoardingNBasicAuthService interface {
|
||||
platform.OnboardingService
|
||||
platform.BasicAuthService
|
||||
}
|
||||
|
||||
// Generate testing
|
||||
func Generate(
|
||||
init func(OnboardingFields, *testing.T) (OnBoardingNBasicAuthService, func()),
|
||||
t *testing.T,
|
||||
) {
|
||||
type args struct {
|
||||
request *platform.OnboardingRequest
|
||||
}
|
||||
type wants struct {
|
||||
errCode string
|
||||
results *platform.OnboardingResults
|
||||
password string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields OnboardingFields
|
||||
args args
|
||||
wants wants
|
||||
}{
|
||||
{
|
||||
name: "denied",
|
||||
fields: OnboardingFields{
|
||||
IDGenerator: &loopIDGenerator{
|
||||
s: []string{oneID, twoID, threeID, fourID},
|
||||
t: t,
|
||||
},
|
||||
TokenGenerator: mock.NewTokenGenerator(oneToken, nil),
|
||||
IsOnboarding: false,
|
||||
},
|
||||
wants: wants{
|
||||
errCode: platform.EConflict,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing password",
|
||||
fields: OnboardingFields{
|
||||
IDGenerator: &loopIDGenerator{
|
||||
s: []string{oneID, twoID, threeID, fourID},
|
||||
t: t,
|
||||
},
|
||||
TokenGenerator: mock.NewTokenGenerator(oneToken, nil),
|
||||
IsOnboarding: true,
|
||||
},
|
||||
args: args{
|
||||
request: &platform.OnboardingRequest{
|
||||
User: "admin",
|
||||
Org: "org1",
|
||||
Bucket: "bucket1",
|
||||
},
|
||||
},
|
||||
wants: wants{
|
||||
errCode: platform.EEmptyValue,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing username",
|
||||
fields: OnboardingFields{
|
||||
IDGenerator: &loopIDGenerator{
|
||||
s: []string{oneID, twoID, threeID, fourID},
|
||||
t: t,
|
||||
},
|
||||
TokenGenerator: mock.NewTokenGenerator(oneToken, nil),
|
||||
IsOnboarding: true,
|
||||
},
|
||||
args: args{
|
||||
request: &platform.OnboardingRequest{
|
||||
Org: "org1",
|
||||
Bucket: "bucket1",
|
||||
},
|
||||
},
|
||||
wants: wants{
|
||||
errCode: platform.EEmptyValue,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing org",
|
||||
fields: OnboardingFields{
|
||||
IDGenerator: &loopIDGenerator{
|
||||
s: []string{oneID, twoID, threeID, fourID},
|
||||
t: t,
|
||||
},
|
||||
TokenGenerator: mock.NewTokenGenerator(oneToken, nil),
|
||||
IsOnboarding: true,
|
||||
},
|
||||
args: args{
|
||||
request: &platform.OnboardingRequest{
|
||||
User: "admin",
|
||||
Bucket: "bucket1",
|
||||
},
|
||||
},
|
||||
wants: wants{
|
||||
errCode: platform.EEmptyValue,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing bucket",
|
||||
fields: OnboardingFields{
|
||||
IDGenerator: &loopIDGenerator{
|
||||
s: []string{oneID, twoID, threeID, fourID},
|
||||
t: t,
|
||||
},
|
||||
TokenGenerator: mock.NewTokenGenerator(oneToken, nil),
|
||||
IsOnboarding: true,
|
||||
},
|
||||
args: args{
|
||||
request: &platform.OnboardingRequest{
|
||||
User: "admin",
|
||||
Org: "org1",
|
||||
},
|
||||
},
|
||||
wants: wants{
|
||||
errCode: platform.EEmptyValue,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "regular",
|
||||
fields: OnboardingFields{
|
||||
IDGenerator: &loopIDGenerator{
|
||||
s: []string{oneID, twoID, threeID, fourID},
|
||||
t: t,
|
||||
},
|
||||
TokenGenerator: mock.NewTokenGenerator(oneToken, nil),
|
||||
IsOnboarding: true,
|
||||
},
|
||||
args: args{
|
||||
request: &platform.OnboardingRequest{
|
||||
User: "admin",
|
||||
Org: "org1",
|
||||
Bucket: "bucket1",
|
||||
Password: "pass1",
|
||||
},
|
||||
},
|
||||
wants: wants{
|
||||
password: "pass1",
|
||||
results: &platform.OnboardingResults{
|
||||
User: &platform.User{
|
||||
ID: idFromString(t, oneID),
|
||||
Name: "admin",
|
||||
},
|
||||
Org: &platform.Organization{
|
||||
ID: idFromString(t, twoID),
|
||||
Name: "org1",
|
||||
},
|
||||
Bucket: &platform.Bucket{
|
||||
ID: idFromString(t, threeID),
|
||||
Name: "bucket1",
|
||||
Organization: "org1",
|
||||
OrganizationID: idFromString(t, twoID),
|
||||
},
|
||||
Auth: &platform.Authorization{
|
||||
ID: idFromString(t, fourID),
|
||||
Token: oneToken,
|
||||
Status: platform.Active,
|
||||
User: "admin",
|
||||
UserID: idFromString(t, oneID),
|
||||
Permissions: []platform.Permission{
|
||||
platform.CreateUserPermission,
|
||||
platform.DeleteUserPermission,
|
||||
platform.Permission{
|
||||
Resource: platform.OrganizationResource,
|
||||
Action: platform.WriteAction,
|
||||
},
|
||||
platform.WriteBucketPermission(idFromString(t, threeID)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s, done := init(tt.fields, t)
|
||||
defer done()
|
||||
ctx := context.Background()
|
||||
results, err := s.Generate(ctx, tt.args.request)
|
||||
if (err != nil) != (tt.wants.errCode != "") {
|
||||
t.Fatalf("expected error code '%s' got '%v'", tt.wants.errCode, err)
|
||||
}
|
||||
if err != nil && tt.wants.errCode != "" {
|
||||
if code := platform.ErrorCode(err); code != tt.wants.errCode {
|
||||
t.Fatalf("expected error code to match '%s' got '%v'", tt.wants.errCode, code)
|
||||
}
|
||||
}
|
||||
if diff := cmp.Diff(results, tt.wants.results); diff != "" {
|
||||
t.Errorf("onboarding results are different -got/+want\ndiff %s", diff)
|
||||
}
|
||||
if results != nil {
|
||||
if err = s.ComparePassword(ctx, results.User.Name, tt.wants.password); err != nil {
|
||||
t.Errorf("onboarding set password is wrong")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const (
|
||||
oneID = "020f755c3c082000"
|
||||
twoID = "020f755c3c082001"
|
||||
threeID = "020f755c3c082002"
|
||||
fourID = "020f755c3c082003"
|
||||
oneToken = "020f755c3c082008"
|
||||
)
|
||||
|
||||
type loopIDGenerator struct {
|
||||
s []string
|
||||
p int
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (g *loopIDGenerator) ID() platform.ID {
|
||||
if g.p == len(g.s) {
|
||||
g.p = 0
|
||||
}
|
||||
id := idFromString(g.t, g.s[g.p])
|
||||
g.p++
|
||||
return id
|
||||
}
|
Loading…
Reference in New Issue