1/*
2Copyright 2017 Google LLC
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package spanner
18
19import (
20	"testing"
21	"time"
22
23	pbd "github.com/golang/protobuf/ptypes/duration"
24	pbt "github.com/golang/protobuf/ptypes/timestamp"
25	sppb "google.golang.org/genproto/googleapis/spanner/v1"
26)
27
28// Test generating TimestampBound for strong reads.
29func TestStrong(t *testing.T) {
30	got := StrongRead()
31	want := TimestampBound{mode: strong}
32	if !testEqual(got, want) {
33		t.Errorf("Strong() = %v; want %v", got, want)
34	}
35}
36
37// Test generating TimestampBound for reads with exact staleness.
38func TestExactStaleness(t *testing.T) {
39	got := ExactStaleness(10 * time.Second)
40	want := TimestampBound{mode: exactStaleness, d: 10 * time.Second}
41	if !testEqual(got, want) {
42		t.Errorf("ExactStaleness(10*time.Second) = %v; want %v", got, want)
43	}
44}
45
46// Test generating TimestampBound for reads with max staleness.
47func TestMaxStaleness(t *testing.T) {
48	got := MaxStaleness(10 * time.Second)
49	want := TimestampBound{mode: maxStaleness, d: 10 * time.Second}
50	if !testEqual(got, want) {
51		t.Errorf("MaxStaleness(10*time.Second) = %v; want %v", got, want)
52	}
53}
54
55// Test generating TimestampBound for reads with minimum freshness requirement.
56func TestMinReadTimestamp(t *testing.T) {
57	ts := time.Now()
58	got := MinReadTimestamp(ts)
59	want := TimestampBound{mode: minReadTimestamp, t: ts}
60	if !testEqual(got, want) {
61		t.Errorf("MinReadTimestamp(%v) = %v; want %v", ts, got, want)
62	}
63}
64
65// Test generating TimestampBound for reads requesting data at a exact timestamp.
66func TestReadTimestamp(t *testing.T) {
67	ts := time.Now()
68	got := ReadTimestamp(ts)
69	want := TimestampBound{mode: readTimestamp, t: ts}
70	if !testEqual(got, want) {
71		t.Errorf("ReadTimestamp(%v) = %v; want %v", ts, got, want)
72	}
73}
74
75// Test TimestampBound.String.
76func TestTimestampBoundString(t *testing.T) {
77	ts := time.Unix(1136239445, 0).UTC()
78	var tests = []struct {
79		tb   TimestampBound
80		want string
81	}{
82		{
83			tb:   TimestampBound{mode: strong},
84			want: "(strong)",
85		},
86		{
87			tb:   TimestampBound{mode: exactStaleness, d: 10 * time.Second},
88			want: "(exactStaleness: 10s)",
89		},
90		{
91			tb:   TimestampBound{mode: maxStaleness, d: 10 * time.Second},
92			want: "(maxStaleness: 10s)",
93		},
94		{
95			tb:   TimestampBound{mode: minReadTimestamp, t: ts},
96			want: "(minReadTimestamp: 2006-01-02 22:04:05 +0000 UTC)",
97		},
98		{
99			tb:   TimestampBound{mode: readTimestamp, t: ts},
100			want: "(readTimestamp: 2006-01-02 22:04:05 +0000 UTC)",
101		},
102	}
103	for _, test := range tests {
104		got := test.tb.String()
105		if got != test.want {
106			t.Errorf("%#v.String():\ngot  %q\nwant %q", test.tb, got, test.want)
107		}
108	}
109}
110
111// Test time.Duration to pdb.Duration conversion.
112func TestDurationProto(t *testing.T) {
113	var tests = []struct {
114		d    time.Duration
115		want pbd.Duration
116	}{
117		{time.Duration(0), pbd.Duration{Seconds: 0, Nanos: 0}},
118		{time.Second, pbd.Duration{Seconds: 1, Nanos: 0}},
119		{time.Millisecond, pbd.Duration{Seconds: 0, Nanos: 1e6}},
120		{15 * time.Nanosecond, pbd.Duration{Seconds: 0, Nanos: 15}},
121		{42 * time.Hour, pbd.Duration{Seconds: 151200}},
122		{-(1*time.Hour + 4*time.Millisecond), pbd.Duration{Seconds: -3600, Nanos: -4e6}},
123	}
124	for _, test := range tests {
125		got := durationProto(test.d)
126		if !testEqual(got, &test.want) {
127			t.Errorf("durationProto(%v) = %v; want %v", test.d, got, test.want)
128		}
129	}
130}
131
132// Test time.Time to pbt.Timestamp conversion.
133func TestTimeProto(t *testing.T) {
134	var tests = []struct {
135		t    time.Time
136		want pbt.Timestamp
137	}{
138		{time.Unix(0, 0), pbt.Timestamp{}},
139		{time.Unix(1136239445, 12345), pbt.Timestamp{Seconds: 1136239445, Nanos: 12345}},
140		{time.Unix(-1000, 12345), pbt.Timestamp{Seconds: -1000, Nanos: 12345}},
141	}
142	for _, test := range tests {
143		got := timestampProto(test.t)
144		if !testEqual(got, &test.want) {
145			t.Errorf("timestampProto(%v) = %v; want %v", test.t, got, test.want)
146		}
147	}
148}
149
150// Test readonly transaction option builder.
151func TestBuildTransactionOptionsReadOnly(t *testing.T) {
152	ts := time.Unix(1136239445, 12345)
153	var tests = []struct {
154		tb   TimestampBound
155		ts   bool
156		want sppb.TransactionOptions_ReadOnly
157	}{
158		{
159			StrongRead(), false,
160			sppb.TransactionOptions_ReadOnly{
161				TimestampBound: &sppb.TransactionOptions_ReadOnly_Strong{
162					Strong: true},
163				ReturnReadTimestamp: false,
164			},
165		},
166		{
167			ExactStaleness(10 * time.Second), true,
168			sppb.TransactionOptions_ReadOnly{
169				TimestampBound: &sppb.TransactionOptions_ReadOnly_ExactStaleness{
170					ExactStaleness: &pbd.Duration{Seconds: 10}},
171				ReturnReadTimestamp: true,
172			},
173		},
174		{
175			MaxStaleness(10 * time.Second), true,
176			sppb.TransactionOptions_ReadOnly{
177				TimestampBound: &sppb.TransactionOptions_ReadOnly_MaxStaleness{
178					MaxStaleness: &pbd.Duration{Seconds: 10}},
179				ReturnReadTimestamp: true,
180			},
181		},
182
183		{
184			MinReadTimestamp(ts), true,
185			sppb.TransactionOptions_ReadOnly{
186				TimestampBound: &sppb.TransactionOptions_ReadOnly_MinReadTimestamp{
187					MinReadTimestamp: &pbt.Timestamp{Seconds: 1136239445, Nanos: 12345}},
188				ReturnReadTimestamp: true,
189			},
190		},
191		{
192			ReadTimestamp(ts), true,
193			sppb.TransactionOptions_ReadOnly{
194				TimestampBound: &sppb.TransactionOptions_ReadOnly_ReadTimestamp{
195					ReadTimestamp: &pbt.Timestamp{Seconds: 1136239445, Nanos: 12345}},
196				ReturnReadTimestamp: true,
197			},
198		},
199	}
200	for _, test := range tests {
201		got := buildTransactionOptionsReadOnly(test.tb, test.ts)
202		if !testEqual(got, &test.want) {
203			t.Errorf("buildTransactionOptionsReadOnly(%v,%v) = %v; want %v", test.tb, test.ts, got, test.want)
204		}
205	}
206}
207