bigtable: Emulator should reject microseconds

BigTable server rejects RowFilters containing a TimestampRange filter
with microsecond precision. The emulator will now also reject these
filters.

Fixes #1676.

Change-Id: Ia2a9e1590fe6ba8a2379283efd9752bcb9d482bc
Reviewed-on: https://code-review.googlesource.com/c/gocloud/+/48811
Reviewed-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Cody Oss <codyoss@google.com>
diff --git a/bigtable/bttest/inmem.go b/bigtable/bttest/inmem.go
index 2bf0422..c377f73 100644
--- a/bigtable/bttest/inmem.go
+++ b/bigtable/bttest/inmem.go
@@ -715,6 +715,10 @@
 		}
 		return inRangeStart() && inRangeEnd(), nil
 	case *btpb.RowFilter_TimestampRangeFilter:
+		// Server should only support millisecond precision.
+		if f.TimestampRangeFilter.StartTimestampMicros%int64(time.Millisecond/time.Microsecond) != 0 || f.TimestampRangeFilter.EndTimestampMicros%int64(time.Millisecond/time.Microsecond) != 0 {
+			return false, status.Errorf(codes.InvalidArgument, "Error in field 'timestamp_range_filter'. Maximum precision allowed in filter is millisecond.\nGot:\nStart: %v\nEnd: %v", f.TimestampRangeFilter.StartTimestampMicros, f.TimestampRangeFilter.EndTimestampMicros)
+		}
 		// Lower bound is inclusive and defaults to 0, upper bound is exclusive and defaults to infinity.
 		return cell.ts >= f.TimestampRangeFilter.StartTimestampMicros &&
 			(f.TimestampRangeFilter.EndTimestampMicros == 0 || cell.ts < f.TimestampRangeFilter.EndTimestampMicros), nil
diff --git a/bigtable/bttest/inmem_test.go b/bigtable/bttest/inmem_test.go
index 5e7dd5d..21ec2d4 100644
--- a/bigtable/bttest/inmem_test.go
+++ b/bigtable/bttest/inmem_test.go
@@ -1367,7 +1367,7 @@
 			"fam": {
 				name: "fam",
 				cells: map[string][]cell{
-					"col": {{ts: 100, value: []byte("val")}},
+					"col": {{ts: 1000, value: []byte("val")}},
 				},
 			},
 		},
@@ -1398,8 +1398,14 @@
 		{&btpb.RowFilter{Filter: &btpb.RowFilter_ValueRegexFilter{[]byte("va")}}, false},
 		{&btpb.RowFilter{Filter: &btpb.RowFilter_ValueRegexFilter{[]byte("VAL")}}, false},
 		{&btpb.RowFilter{Filter: &btpb.RowFilter_ValueRegexFilter{[]byte("moo")}}, false},
+
+		{&btpb.RowFilter{Filter: &btpb.RowFilter_TimestampRangeFilter{&btpb.TimestampRange{StartTimestampMicros: int64(0), EndTimestampMicros: int64(1000)}}}, false},
+		{&btpb.RowFilter{Filter: &btpb.RowFilter_TimestampRangeFilter{&btpb.TimestampRange{StartTimestampMicros: int64(1000), EndTimestampMicros: int64(2000)}}}, true},
 	} {
-		got, _ := filterRow(test.filter, row.copy())
+		got, err := filterRow(test.filter, row.copy())
+		if err != nil {
+			t.Errorf("%s: got unexpected error: %v", proto.CompactTextString(test.filter), err)
+		}
 		if got != test.want {
 			t.Errorf("%s: got %t, want %t", proto.CompactTextString(test.filter), got, test.want)
 		}
@@ -1413,7 +1419,7 @@
 			"fam": {
 				name: "fam",
 				cells: map[string][]cell{
-					"col": {{ts: 100, value: []byte("val")}},
+					"col": {{ts: 1000, value: []byte("val")}},
 				},
 			},
 		},
@@ -1436,8 +1442,10 @@
 			},
 		}}},
 
-		{&btpb.RowFilter{Filter: &btpb.RowFilter_RowSampleFilter{0.0}}}, // 0.0 is invalid.
-		{&btpb.RowFilter{Filter: &btpb.RowFilter_RowSampleFilter{1.0}}}, // 1.0 is invalid.
+		{&btpb.RowFilter{Filter: &btpb.RowFilter_RowSampleFilter{0.0}}},                                                                                        // 0.0 is invalid.
+		{&btpb.RowFilter{Filter: &btpb.RowFilter_RowSampleFilter{1.0}}},                                                                                        // 1.0 is invalid.
+		{&btpb.RowFilter{Filter: &btpb.RowFilter_TimestampRangeFilter{&btpb.TimestampRange{StartTimestampMicros: int64(1), EndTimestampMicros: int64(1000)}}}}, // Server only supports millisecond precision.
+		{&btpb.RowFilter{Filter: &btpb.RowFilter_TimestampRangeFilter{&btpb.TimestampRange{StartTimestampMicros: int64(1000), EndTimestampMicros: int64(1)}}}}, // Server only supports millisecond precision.
 	} {
 		got, err := filterRow(test.badRegex, row.copy())
 		if got != false {
@@ -1479,7 +1487,7 @@
 			"fam": {
 				name: "fam",
 				cells: map[string][]cell{
-					string(rs): {{ts: 100, value: []byte("val")}},
+					string(rs): {{ts: 1000, value: []byte("val")}},
 				},
 			},
 		},