bug(session): Allow only valid state transitions
This commit is contained in:
1 parent
f8b2e99e11
commit
edae7c899f
15 files changed
+441
-11
No files matched your search
@@ -31,6 +31,9 @@ Canonical reference for changes, improvements, and bugfixes for Boundary.
|
||||
* plugins/aws: AWS plugin based hosts now include DNS names in addition to the
|
||||
IP addresses they already provide.
|
||||
|
||||
### Bug Fixes
|
||||
* session: Fix duplicate sessions and invalid session state transitions. ([PR](https://github.com/hashicorp/boundary/pull/1793))
|
||||
|
||||
## 0.7.3 (2021/12/16)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
begin;
|
||||
|
||||
-- Remove invalid (states with no prior end time) rows
|
||||
delete from session_state where previous_end_time is null and state != 'pending';
|
||||
|
||||
-- In the case of dupes that are otherwise valid, identify first valid session state and remove others
|
||||
with cte_session_state_delete
|
||||
as (select session_id, min(start_time) as st_t, state from session_state ou
|
||||
where (select count(*) from session_state inr
|
||||
where inr.session_id = ou.session_id and inr.state = ou.state) > 1
|
||||
group by session_id, state)
|
||||
delete from session_state t1
|
||||
using cte_session_state_delete t2 where t1.session_id =t2.session_id and t1.start_time > t2.st_t and t1.state=t2.state;
|
||||
|
||||
-- Close all open session_connections
|
||||
update session_connection
|
||||
set closed_reason='canceled' where closed_reason is null;
|
||||
|
||||
update session
|
||||
set termination_reason='canceled' where termination_reason is null;
|
||||
|
||||
-- End of data cleanup; start of table modifications
|
||||
|
||||
-- session_valid_state table creation and related constraints
|
||||
create table session_valid_state(
|
||||
prior_state text
|
||||
references session_state_enm(name)
|
||||
on delete restrict
|
||||
on update cascade,
|
||||
constraint prior_state_session_state_enm_fkey
|
||||
check (
|
||||
prior_state in ('pending', 'active', 'canceling')
|
||||
),
|
||||
current_state text
|
||||
references session_state_enm(name)
|
||||
on delete restrict
|
||||
on update cascade,
|
||||
constraint current_state_session_state_enm_fkey
|
||||
check (
|
||||
current_state in ('pending', 'active', 'canceling', 'terminated')
|
||||
),
|
||||
primary key (prior_state, current_state)
|
||||
);
|
||||
comment on table session_valid_state is
|
||||
'session_valid_state entries define valid prior_state and current_state pairs to define valid state transitions';
|
||||
|
||||
insert into session_valid_state (prior_state, current_state)
|
||||
values
|
||||
('pending','pending'),
|
||||
('pending','active'),
|
||||
('pending','terminated'),
|
||||
('pending','canceling'),
|
||||
('active','canceling'),
|
||||
('active','terminated'),
|
||||
('canceling','terminated');
|
||||
|
||||
alter table session_state
|
||||
add column prior_state text not null default 'pending'
|
||||
references session_state_enm(name)
|
||||
on delete restrict
|
||||
on update cascade;
|
||||
alter table session_state
|
||||
add constraint session_valid_state_enm_fkey
|
||||
foreign key (prior_state, state)
|
||||
references session_valid_state (prior_state,current_state);
|
||||
alter table session_state
|
||||
add unique (session_id, state);
|
||||
|
||||
create function
|
||||
update_prior_session_state()
|
||||
returns trigger
|
||||
as $$
|
||||
begin
|
||||
-- Prior state is the most recent valid prior state entry for this session_id
|
||||
new.prior_state = query.state from(
|
||||
select state from session_state where session_id=new.session_id and state in(
|
||||
select prior_state from session_valid_state where current_state=new.state )
|
||||
order by start_time desc limit 1) as query;
|
||||
|
||||
if new.prior_state is null then
|
||||
new.prior_state='pending';
|
||||
end if;
|
||||
|
||||
return new;
|
||||
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
create trigger update_session_state before insert on session_state
|
||||
for each row execute procedure update_prior_session_state();
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,165 @@
|
||||
package oss_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/authtoken"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/db/common"
|
||||
"github.com/hashicorp/boundary/internal/db/schema"
|
||||
"github.com/hashicorp/boundary/internal/host/static"
|
||||
"github.com/hashicorp/boundary/internal/iam"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
"github.com/hashicorp/boundary/internal/session"
|
||||
"github.com/hashicorp/boundary/internal/target"
|
||||
"github.com/hashicorp/boundary/internal/target/tcp"
|
||||
"github.com/hashicorp/boundary/testing/dbtest"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMigrations_SessionState(t *testing.T) {
|
||||
t.Parallel()
|
||||
require := require.New(t)
|
||||
|
||||
const (
|
||||
priorMigration = 21002
|
||||
currentMigration = 24001
|
||||
)
|
||||
dialect := dbtest.Postgres
|
||||
ctx := context.Background()
|
||||
|
||||
c, u, _, err := dbtest.StartUsingTemplate(dialect, dbtest.WithTemplate(dbtest.Template1))
|
||||
require.NoError(err)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(c())
|
||||
})
|
||||
d, err := common.SqlOpen(dialect, u)
|
||||
require.NoError(err)
|
||||
|
||||
// migration to the prior migration (before the one we want to test)
|
||||
m, err := schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": priorMigration}),
|
||||
))
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(m.ApplyMigrations(ctx))
|
||||
state, err := m.CurrentState(ctx)
|
||||
require.NoError(err)
|
||||
want := &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: priorMigration,
|
||||
DatabaseSchemaVersion: priorMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(want, state)
|
||||
|
||||
// Seed the database with test data
|
||||
dbType, err := db.StringToDbType(dialect)
|
||||
require.NoError(err)
|
||||
|
||||
conn, err := db.Open(dbType, u)
|
||||
require.NoError(err)
|
||||
|
||||
rw := db.New(conn)
|
||||
wrapper := db.TestWrapper(t)
|
||||
|
||||
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
require.NotNil(prj)
|
||||
|
||||
hc := static.TestCatalogs(t, conn, prj.GetPublicId(), 1)[0]
|
||||
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
|
||||
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
|
||||
static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h})
|
||||
|
||||
tar := tcp.TestTarget(ctx, t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()}))
|
||||
|
||||
kmsCache := kms.TestKms(t, conn, wrapper)
|
||||
|
||||
{
|
||||
at := authtoken.TestAuthToken(t, conn, kmsCache, org.GetPublicId())
|
||||
uId := at.GetIamUserId()
|
||||
|
||||
sess := session.TestSession(t, conn, wrapper, session.ComposedOf{
|
||||
UserId: uId,
|
||||
HostId: h.GetPublicId(),
|
||||
TargetId: tar.GetPublicId(),
|
||||
HostSetId: hs.GetPublicId(),
|
||||
AuthTokenId: at.GetPublicId(),
|
||||
ScopeId: prj.GetPublicId(),
|
||||
Endpoint: "tcp://127.0.0.1:22",
|
||||
ConnectionLimit: -1,
|
||||
})
|
||||
|
||||
session.TestConnection(t, conn, sess.PublicId, "127.0.0.1", 22,
|
||||
"127.0.0.2", 23, "127.0.0.1")
|
||||
}
|
||||
|
||||
// now we're ready for the migration we want to test.
|
||||
m, err = schema.NewManager(ctx, schema.Dialect(dialect), d, schema.WithEditions(
|
||||
schema.TestCreatePartialEditions(schema.Dialect(dialect), schema.PartialEditions{"oss": currentMigration}),
|
||||
))
|
||||
require.NoError(err)
|
||||
|
||||
require.NoError(m.ApplyMigrations(ctx))
|
||||
state, err = m.CurrentState(ctx)
|
||||
require.NoError(err)
|
||||
want = &schema.State{
|
||||
Initialized: true,
|
||||
Editions: []schema.EditionState{
|
||||
{
|
||||
Name: "oss",
|
||||
BinarySchemaVersion: currentMigration,
|
||||
DatabaseSchemaVersion: currentMigration,
|
||||
DatabaseSchemaState: schema.Equal,
|
||||
},
|
||||
},
|
||||
}
|
||||
require.Equal(want, state)
|
||||
|
||||
sessionRepo, err := session.NewRepository(rw, rw, kmsCache)
|
||||
require.NoError(err)
|
||||
|
||||
// Ensure session is cancelled
|
||||
repoSessions, err := sessionRepo.ListSessions(ctx)
|
||||
require.NoError(err)
|
||||
var sessionTermReason []string
|
||||
var repoSessionId string
|
||||
for i := 0; i < len(repoSessions); i++ {
|
||||
s := repoSessions[i]
|
||||
repoSessionId = s.PublicId
|
||||
sessionTermReason = append(sessionTermReason, s.TerminationReason)
|
||||
}
|
||||
require.Equal([]string{"canceled"}, sessionTermReason)
|
||||
|
||||
// Ensure connection is also cancelled
|
||||
connections, err := sessionRepo.ListConnectionsBySessionId(ctx, repoSessionId)
|
||||
require.NoError(err)
|
||||
|
||||
var connTermReason []string
|
||||
for i := 0; i < len(connections); i++ {
|
||||
c := connections[i]
|
||||
connTermReason = append(connTermReason, c.ClosedReason)
|
||||
}
|
||||
require.Equal([]string{"canceled"}, connTermReason)
|
||||
|
||||
// Validate new table contents
|
||||
rows, err := d.QueryContext(ctx, "select * from session_valid_state")
|
||||
require.NoError(err)
|
||||
|
||||
var validStates []string
|
||||
for rows.Next() {
|
||||
var a, b string
|
||||
require.NoError(rows.Scan(&a, &b))
|
||||
validStates = append(validStates, []string{a, b}...)
|
||||
}
|
||||
require.Equal([]string{
|
||||
"pending", "pending", "pending", "active", "pending", "terminated",
|
||||
"pending", "canceling", "active", "canceling", "active", "terminated", "canceling", "terminated",
|
||||
}, validStates)
|
||||
}
|
||||
@@ -120,7 +120,10 @@ begin;
|
||||
insert into auth_token
|
||||
(key_id, auth_account_id, public_id, token)
|
||||
values
|
||||
('key', 'apa____clare', 'tok____clare', 'tok____clare'::bytea);
|
||||
('key', 'apa____clare', 'tok____clare', 'tok____clare'::bytea),
|
||||
('key', 'apa____cindy', 'tok____cindy', 'tok____cindy'::bytea),
|
||||
('key', 'apa____ciara', 'tok____ciara', 'tok____ciara'::bytea),
|
||||
('key', 'apa____carly', 'tok____carly', 'tok____carly'::bytea);
|
||||
|
||||
insert into static_host
|
||||
(catalog_id, public_id, address)
|
||||
@@ -194,7 +197,10 @@ begin;
|
||||
insert into session
|
||||
( scope_id, target_id, host_set_id, host_id, user_id, auth_token_id, certificate, endpoint, public_id)
|
||||
values
|
||||
('p____bcolors', 't_________cb', 's___1cb-sths', 'h_____cb__01', 'u______clare', 'tok____clare', 'abc'::bytea, 'ep1', 's1_____clare');
|
||||
('p____bcolors', 't_________cb', 's___1cb-sths', 'h_____cb__01', 'u______clare', 'tok____clare', 'abc'::bytea, 'ep1', 's1_____clare'),
|
||||
('p____bcolors', 't_________cb', 's___1cb-sths', 'h_____cb__01', 'u______cindy', 'tok____cindy', 'abc'::bytea, 'ep1', 's1_____cindy'),
|
||||
('p____bcolors', 't_________cb', 's___1cb-sths', 'h_____cb__01', 'u______cindy', 'tok____cindy', 'abc'::bytea, 'ep1', 's1_____ciara'),
|
||||
('p____bcolors', 't_________cb', 's___1cb-sths', 'h_____cb__01', 'u______carly', 'tok____carly', 'abc'::bytea, 'ep1', 's1_____carly');
|
||||
|
||||
insert into session_connection
|
||||
(session_id, public_id)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
begin;
|
||||
|
||||
-- For use in testing session_state transitions
|
||||
insert into session_state
|
||||
(session_id, state)
|
||||
values
|
||||
('s1_____cindy','terminated'),
|
||||
('s1_____ciara','canceling'),
|
||||
('s1_____carly','active');
|
||||
|
||||
commit;
|
||||
@@ -358,4 +358,16 @@ begin;
|
||||
('t_________wb', 'vl______wvl3', 'egress');
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
create function _wtt_load_widgets_sessions()
|
||||
returns void
|
||||
as $$
|
||||
begin
|
||||
insert into session
|
||||
( scope_id , target_id , host_set_id , host_id , user_id , auth_token_id , certificate , endpoint , public_id)
|
||||
values
|
||||
('p____bwidget' , 't_________wb' , 's___1wb-sths' , 'h_____wb__01' , 'u_____warren' , 'tok___warren' , 'abc'::bytea , 'ep1' , 's1____warren');
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,43 @@
|
||||
begin;
|
||||
select plan(12);
|
||||
|
||||
-- Ensure session state table is populated
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='pending';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____cindy' and state='terminated';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____ciara' and state='canceling';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____carly' and state='active';
|
||||
|
||||
-- Invalid duplicate state
|
||||
select throws_ok($$ insert into session_state ( session_id, state )
|
||||
values ('s1_____clare','pending')$$);
|
||||
|
||||
-- Invalid state transition from terminated to pending
|
||||
select throws_ok($$ insert into session_state ( session_id, state)
|
||||
values ('s1______cindy','pending')$$);
|
||||
|
||||
-- Invalid state transition from terminated to active
|
||||
select throws_ok($$ insert into session_state ( session_id, state)
|
||||
values ('s1______cindy','active')$$);
|
||||
|
||||
-- Invalid state transition from terminated to canceling
|
||||
select throws_ok($$ insert into session_state ( session_id, state)
|
||||
values ('s1______cindy','canceling')$$);
|
||||
|
||||
-- Invalid state transition from terminated to terminated
|
||||
select throws_ok($$ insert into session_state ( session_id, state)
|
||||
values ('s1______cindy','terminated')$$);
|
||||
|
||||
-- Invalid state transition from active to pending
|
||||
select throws_ok($$ insert into session_state ( session_id, state)
|
||||
values ('s1______carly','pending')$$);
|
||||
|
||||
-- Invalid state transition from canceling to pending
|
||||
select throws_ok($$ insert into session_state ( session_id, state)
|
||||
values ('s1______ciara','pending')$$);
|
||||
|
||||
-- Invalid state transition from canceling to active
|
||||
select throws_ok($$ insert into session_state ( session_id, state)
|
||||
values ('s1______ciara','active')$$);
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
@@ -0,0 +1,28 @@
|
||||
begin;
|
||||
select plan(7);
|
||||
|
||||
-- Ensure session state table is populated
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='pending';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____carly' and state='active';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____ciara' and state='canceling';
|
||||
|
||||
-- Valid state transition from pending to canceling
|
||||
insert into session_state
|
||||
( session_id, state)
|
||||
values
|
||||
('s1_____clare','canceling');
|
||||
select is(count(*), 2::bigint) from session_state where session_id = 's1_____clare';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='canceling';
|
||||
|
||||
|
||||
-- Valid state transition from active to canceling
|
||||
insert into session_state
|
||||
( session_id, state)
|
||||
values
|
||||
('s1_____carly','canceling');
|
||||
select is(count(*), 3::bigint) from session_state where session_id = 's1_____carly';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____carly' and state='canceling';
|
||||
|
||||
select * from finish();
|
||||
|
||||
rollback;
|
||||
@@ -0,0 +1,17 @@
|
||||
begin;
|
||||
select plan(3);
|
||||
|
||||
-- Ensure session state table is populated
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='pending';
|
||||
|
||||
-- Valid state transition from pending to active
|
||||
insert into session_state
|
||||
( session_id, state)
|
||||
values
|
||||
('s1_____clare','active');
|
||||
|
||||
select is(count(*), 2::bigint) from session_state where session_id = 's1_____clare';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='active';
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
@@ -0,0 +1,17 @@
|
||||
begin;
|
||||
select plan(3);
|
||||
|
||||
-- Ensure session state table is populated
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='pending';
|
||||
|
||||
-- Valid state transition from pending to canceling
|
||||
insert into session_state
|
||||
( session_id, state)
|
||||
values
|
||||
('s1_____clare','canceling');
|
||||
|
||||
select is(count(*), 2::bigint) from session_state where session_id = 's1_____clare';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='canceling';
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
@@ -0,0 +1,35 @@
|
||||
begin;
|
||||
select plan(9);
|
||||
|
||||
-- Ensure session state table is populated
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='pending';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____carly' and state='active';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____ciara' and state='canceling';
|
||||
|
||||
-- Valid state transition from pending to terminated
|
||||
insert into session_state
|
||||
( session_id, state)
|
||||
values
|
||||
('s1_____clare','terminated');
|
||||
select is(count(*), 2::bigint) from session_state where session_id = 's1_____clare';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____clare' and state='terminated';
|
||||
|
||||
|
||||
-- Valid state transition from active to terminated
|
||||
insert into session_state
|
||||
( session_id, state)
|
||||
values
|
||||
('s1_____carly','terminated');
|
||||
select is(count(*), 3::bigint) from session_state where session_id = 's1_____carly';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____carly' and state='terminated';
|
||||
|
||||
-- Valid state transition from canceling to terminated
|
||||
insert into session_state
|
||||
( session_id, state)
|
||||
values
|
||||
('s1_____ciara','terminated');
|
||||
select is(count(*), 3::bigint) from session_state where session_id = 's1_____ciara';
|
||||
select is(count(*), 1::bigint) from session_state where session_id = 's1_____ciara' and state='terminated';
|
||||
|
||||
select * from finish();
|
||||
rollback;
|
||||
@@ -15,7 +15,7 @@ begin;
|
||||
termination_reason = 'closed by end-user'
|
||||
where public_id = 's1_____clare';
|
||||
|
||||
select is(count(*), 1::bigint) from wh_session_accumulating_fact;
|
||||
select is(count(*), 4::bigint) from wh_session_accumulating_fact;
|
||||
select is(total_bytes_up, 10::wh_bytes_transmitted) from wh_session_accumulating_fact where session_id = 's1_____clare';
|
||||
select is(total_bytes_down, 5::wh_bytes_transmitted) from wh_session_accumulating_fact where session_id = 's1_____clare';
|
||||
select is(session_terminated_time, now()::wh_timestamp) from wh_session_accumulating_fact where session_id = 's1_____clare';
|
||||
|
||||
@@ -99,7 +99,7 @@ func TestState_ImmutableFields(t *testing.T) {
|
||||
|
||||
_, _ = iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
|
||||
session := TestDefaultSession(t, conn, wrapper, iamRepo)
|
||||
state := TestState(t, conn, session.PublicId, StatusPending)
|
||||
state := TestState(t, conn, session.PublicId, StatusActive)
|
||||
|
||||
var new State
|
||||
err := rw.LookupWhere(context.Background(), &new, "session_id = ? and state = ?", []interface{}{state.SessionId, state.Status})
|
||||
@@ -123,7 +123,7 @@ func TestState_ImmutableFields(t *testing.T) {
|
||||
name: "status",
|
||||
update: func() *State {
|
||||
s := new.Clone().(*State)
|
||||
s.Status = "active"
|
||||
s.Status = "canceling"
|
||||
return s
|
||||
}(),
|
||||
fieldMask: []string{"Status"},
|
||||
|
||||
@@ -22,6 +22,7 @@ func TestState_Create(t *testing.T) {
|
||||
sessionId string
|
||||
status Status
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
@@ -35,11 +36,11 @@ func TestState_Create(t *testing.T) {
|
||||
name: "valid",
|
||||
args: args{
|
||||
sessionId: session.PublicId,
|
||||
status: StatusPending,
|
||||
status: StatusActive,
|
||||
},
|
||||
want: &State{
|
||||
SessionId: session.PublicId,
|
||||
Status: StatusPending,
|
||||
Status: StatusActive,
|
||||
},
|
||||
create: true,
|
||||
},
|
||||
@@ -161,7 +162,7 @@ func TestState_Clone(t *testing.T) {
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
s := TestDefaultSession(t, conn, wrapper, iamRepo)
|
||||
state := TestState(t, conn, s.PublicId, StatusPending)
|
||||
state := TestState(t, conn, s.PublicId, StatusActive)
|
||||
cp := state.Clone()
|
||||
assert.Equal(cp.(*State), state)
|
||||
})
|
||||
@@ -169,8 +170,8 @@ func TestState_Clone(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
s := TestDefaultSession(t, conn, wrapper, iamRepo)
|
||||
s2 := TestDefaultSession(t, conn, wrapper, iamRepo)
|
||||
state := TestState(t, conn, s.PublicId, StatusPending)
|
||||
state2 := TestState(t, conn, s2.PublicId, StatusPending)
|
||||
state := TestState(t, conn, s.PublicId, StatusActive)
|
||||
state2 := TestState(t, conn, s2.PublicId, StatusActive)
|
||||
|
||||
cp := state.Clone()
|
||||
assert.NotEqual(cp.(*State), state2)
|
||||
|
||||
@@ -29,7 +29,7 @@ func Test_TestState(t *testing.T) {
|
||||
require.NotNil(s)
|
||||
assert.NotEmpty(s.PublicId)
|
||||
|
||||
state := TestState(t, conn, s.PublicId, StatusPending)
|
||||
state := TestState(t, conn, s.PublicId, StatusActive)
|
||||
require.NotNil(state)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user