1package statemgr
2
3import (
4	"context"
5	"encoding/json"
6	"flag"
7	"os"
8	"testing"
9	"time"
10
11	_ "github.com/hashicorp/terraform/internal/logging"
12)
13
14func TestNewLockInfo(t *testing.T) {
15	info1 := NewLockInfo()
16	info2 := NewLockInfo()
17
18	if info1.ID == "" {
19		t.Fatal("LockInfo missing ID")
20	}
21
22	if info1.Version == "" {
23		t.Fatal("LockInfo missing version")
24	}
25
26	if info1.Created.IsZero() {
27		t.Fatal("LockInfo missing Created")
28	}
29
30	if info1.ID == info2.ID {
31		t.Fatal("multiple LockInfo with identical IDs")
32	}
33
34	// test the JSON output is valid
35	newInfo := &LockInfo{}
36	err := json.Unmarshal(info1.Marshal(), newInfo)
37	if err != nil {
38		t.Fatal(err)
39	}
40}
41
42func TestLockWithContext(t *testing.T) {
43	s := NewFullFake(nil, TestFullInitialState())
44
45	id, err := s.Lock(NewLockInfo())
46	if err != nil {
47		t.Fatal(err)
48	}
49
50	// use a cancelled context for an immediate timeout
51	ctx, cancel := context.WithCancel(context.Background())
52	cancel()
53
54	info := NewLockInfo()
55	info.Info = "lock with context"
56	_, err = LockWithContext(ctx, s, info)
57	if err == nil {
58		t.Fatal("lock should have failed immediately")
59	}
60
61	// block until LockwithContext has made a first attempt
62	attempted := make(chan struct{})
63	postLockHook = func() {
64		close(attempted)
65		postLockHook = nil
66	}
67
68	// unlock the state during LockWithContext
69	unlocked := make(chan struct{})
70	var unlockErr error
71	go func() {
72		defer close(unlocked)
73		<-attempted
74		unlockErr = s.Unlock(id)
75	}()
76
77	ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
78	defer cancel()
79
80	id, err = LockWithContext(ctx, s, info)
81	if err != nil {
82		t.Fatal("lock should have completed within 2s:", err)
83	}
84
85	// ensure the goruotine completes
86	<-unlocked
87	if unlockErr != nil {
88		t.Fatal(unlockErr)
89	}
90}
91
92func TestMain(m *testing.M) {
93	flag.Parse()
94	os.Exit(m.Run())
95}
96