1// Copyright 2016 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package e2e
16
17import "testing"
18
19func TestCtlV3TxnInteractiveSuccess(t *testing.T) {
20	testCtl(t, txnTestSuccess, withInteractive())
21}
22func TestCtlV3TxnInteractiveSuccessNoTLS(t *testing.T) {
23	testCtl(t, txnTestSuccess, withInteractive(), withCfg(configNoTLS))
24}
25func TestCtlV3TxnInteractiveSuccessClientTLS(t *testing.T) {
26	testCtl(t, txnTestSuccess, withInteractive(), withCfg(configClientTLS))
27}
28func TestCtlV3TxnInteractiveSuccessPeerTLS(t *testing.T) {
29	testCtl(t, txnTestSuccess, withInteractive(), withCfg(configPeerTLS))
30}
31func TestCtlV3TxnInteractiveFail(t *testing.T) {
32	testCtl(t, txnTestFail, withInteractive())
33}
34
35func txnTestSuccess(cx ctlCtx) {
36	if err := ctlV3Put(cx, "key1", "value1", ""); err != nil {
37		cx.t.Fatalf("txnTestSuccess ctlV3Put error (%v)", err)
38	}
39	if err := ctlV3Put(cx, "key2", "value2", ""); err != nil {
40		cx.t.Fatalf("txnTestSuccess ctlV3Put error (%v)", err)
41	}
42	rqs := []txnRequests{
43		{
44			compare:  []string{`value("key1") != "value2"`, `value("key2") != "value1"`},
45			ifSucess: []string{"get key1", "get key2"},
46			results:  []string{"SUCCESS", "key1", "value1", "key2", "value2"},
47		},
48		{
49			compare:  []string{`version("key1") = "1"`, `version("key2") = "1"`},
50			ifSucess: []string{"get key1", "get key2", `put "key \"with\" space" "value \x23"`},
51			ifFail:   []string{`put key1 "fail"`, `put key2 "fail"`},
52			results:  []string{"SUCCESS", "key1", "value1", "key2", "value2"},
53		},
54		{
55			compare:  []string{`version("key \"with\" space") = "1"`},
56			ifSucess: []string{`get "key \"with\" space"`},
57			results:  []string{"SUCCESS", `key "with" space`, "value \x23"},
58		},
59	}
60	for _, rq := range rqs {
61		if err := ctlV3Txn(cx, rq); err != nil {
62			cx.t.Fatal(err)
63		}
64	}
65}
66
67func txnTestFail(cx ctlCtx) {
68	if err := ctlV3Put(cx, "key1", "value1", ""); err != nil {
69		cx.t.Fatalf("txnTestSuccess ctlV3Put error (%v)", err)
70	}
71	rqs := []txnRequests{
72		{
73			compare:  []string{`version("key") < "0"`},
74			ifSucess: []string{`put key "success"`},
75			ifFail:   []string{`put key "fail"`},
76			results:  []string{"FAILURE", "OK"},
77		},
78		{
79			compare:  []string{`value("key1") != "value1"`},
80			ifSucess: []string{`put key1 "success"`},
81			ifFail:   []string{`put key1 "fail"`},
82			results:  []string{"FAILURE", "OK"},
83		},
84	}
85	for _, rq := range rqs {
86		if err := ctlV3Txn(cx, rq); err != nil {
87			cx.t.Fatal(err)
88		}
89	}
90}
91
92type txnRequests struct {
93	compare  []string
94	ifSucess []string
95	ifFail   []string
96	results  []string
97}
98
99func ctlV3Txn(cx ctlCtx, rqs txnRequests) error {
100	// TODO: support non-interactive mode
101	cmdArgs := append(cx.PrefixArgs(), "txn")
102	if cx.interactive {
103		cmdArgs = append(cmdArgs, "--interactive")
104	}
105	proc, err := spawnCmd(cmdArgs)
106	if err != nil {
107		return err
108	}
109	_, err = proc.Expect("compares:")
110	if err != nil {
111		return err
112	}
113	for _, req := range rqs.compare {
114		if err = proc.Send(req + "\r"); err != nil {
115			return err
116		}
117	}
118	if err = proc.Send("\r"); err != nil {
119		return err
120	}
121
122	_, err = proc.Expect("success requests (get, put, delete):")
123	if err != nil {
124		return err
125	}
126	for _, req := range rqs.ifSucess {
127		if err = proc.Send(req + "\r"); err != nil {
128			return err
129		}
130	}
131	if err = proc.Send("\r"); err != nil {
132		return err
133	}
134
135	_, err = proc.Expect("failure requests (get, put, delete):")
136	if err != nil {
137		return err
138	}
139	for _, req := range rqs.ifFail {
140		if err = proc.Send(req + "\r"); err != nil {
141			return err
142		}
143	}
144	if err = proc.Send("\r"); err != nil {
145		return err
146	}
147
148	for _, line := range rqs.results {
149		_, err = proc.Expect(line)
150		if err != nil {
151			return err
152		}
153	}
154	return proc.Close()
155}
156