Merge branch '11645-keepstore-storageclasses' closes #11645
[arvados.git] / services / keepstore / handlers_with_generic_volume_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package main
6
7 import (
8         "bytes"
9         "context"
10 )
11
12 // A TestableVolumeManagerFactory creates a volume manager with at least two TestableVolume instances.
13 // The factory function, and the TestableVolume instances it returns, can use "t" to write
14 // logs, fail the current test, etc.
15 type TestableVolumeManagerFactory func(t TB) (*RRVolumeManager, []TestableVolume)
16
17 // DoHandlersWithGenericVolumeTests runs a set of handler tests with a
18 // Volume Manager comprised of TestableVolume instances.
19 // It calls factory to create a volume manager with TestableVolume
20 // instances for each test case, to avoid leaking state between tests.
21 func DoHandlersWithGenericVolumeTests(t TB, factory TestableVolumeManagerFactory) {
22         testGetBlock(t, factory, TestHash, TestBlock)
23         testGetBlock(t, factory, EmptyHash, EmptyBlock)
24         testPutRawBadDataGetBlock(t, factory, TestHash, TestBlock, []byte("baddata"))
25         testPutRawBadDataGetBlock(t, factory, EmptyHash, EmptyBlock, []byte("baddata"))
26         testPutBlock(t, factory, TestHash, TestBlock)
27         testPutBlock(t, factory, EmptyHash, EmptyBlock)
28         testPutBlockCorrupt(t, factory, TestHash, TestBlock, []byte("baddata"))
29         testPutBlockCorrupt(t, factory, EmptyHash, EmptyBlock, []byte("baddata"))
30 }
31
32 // Setup RRVolumeManager with TestableVolumes
33 func setupHandlersWithGenericVolumeTest(t TB, factory TestableVolumeManagerFactory) []TestableVolume {
34         vm, testableVolumes := factory(t)
35         KeepVM = vm
36
37         for _, v := range testableVolumes {
38                 defer v.Teardown()
39         }
40         defer KeepVM.Close()
41
42         return testableVolumes
43 }
44
45 // Put a block using PutRaw in just one volume and Get it using GetBlock
46 func testGetBlock(t TB, factory TestableVolumeManagerFactory, testHash string, testBlock []byte) {
47         testableVolumes := setupHandlersWithGenericVolumeTest(t, factory)
48
49         // Put testBlock in one volume
50         testableVolumes[1].PutRaw(testHash, testBlock)
51
52         // Get should pass
53         buf := make([]byte, len(testBlock))
54         n, err := GetBlock(context.Background(), testHash, buf, nil)
55         if err != nil {
56                 t.Fatalf("Error while getting block %s", err)
57         }
58         if bytes.Compare(buf[:n], testBlock) != 0 {
59                 t.Errorf("Put succeeded but Get returned %+v, expected %+v", buf[:n], testBlock)
60         }
61 }
62
63 // Put a bad block using PutRaw and get it.
64 func testPutRawBadDataGetBlock(t TB, factory TestableVolumeManagerFactory,
65         testHash string, testBlock []byte, badData []byte) {
66         testableVolumes := setupHandlersWithGenericVolumeTest(t, factory)
67
68         // Put bad data for testHash in both volumes
69         testableVolumes[0].PutRaw(testHash, badData)
70         testableVolumes[1].PutRaw(testHash, badData)
71
72         // Get should fail
73         buf := make([]byte, BlockSize)
74         size, err := GetBlock(context.Background(), testHash, buf, nil)
75         if err == nil {
76                 t.Fatalf("Got %+q, expected error while getting corrupt block %v", buf[:size], testHash)
77         }
78 }
79
80 // Invoke PutBlock twice to ensure CompareAndTouch path is tested.
81 func testPutBlock(t TB, factory TestableVolumeManagerFactory, testHash string, testBlock []byte) {
82         setupHandlersWithGenericVolumeTest(t, factory)
83
84         // PutBlock
85         if _, err := PutBlock(context.Background(), testBlock, testHash); err != nil {
86                 t.Fatalf("Error during PutBlock: %s", err)
87         }
88
89         // Check that PutBlock succeeds again even after CompareAndTouch
90         if _, err := PutBlock(context.Background(), testBlock, testHash); err != nil {
91                 t.Fatalf("Error during PutBlock: %s", err)
92         }
93
94         // Check that PutBlock stored the data as expected
95         buf := make([]byte, BlockSize)
96         size, err := GetBlock(context.Background(), testHash, buf, nil)
97         if err != nil {
98                 t.Fatalf("Error during GetBlock for %q: %s", testHash, err)
99         } else if bytes.Compare(buf[:size], testBlock) != 0 {
100                 t.Errorf("Get response incorrect. Expected %q; found %q", testBlock, buf[:size])
101         }
102 }
103
104 // Put a bad block using PutRaw, overwrite it using PutBlock and get it.
105 func testPutBlockCorrupt(t TB, factory TestableVolumeManagerFactory,
106         testHash string, testBlock []byte, badData []byte) {
107         testableVolumes := setupHandlersWithGenericVolumeTest(t, factory)
108
109         // Put bad data for testHash in both volumes
110         testableVolumes[0].PutRaw(testHash, badData)
111         testableVolumes[1].PutRaw(testHash, badData)
112
113         // Check that PutBlock with good data succeeds
114         if _, err := PutBlock(context.Background(), testBlock, testHash); err != nil {
115                 t.Fatalf("Error during PutBlock for %q: %s", testHash, err)
116         }
117
118         // Put succeeded and overwrote the badData in one volume,
119         // and Get should return the testBlock now, ignoring the bad data.
120         buf := make([]byte, BlockSize)
121         size, err := GetBlock(context.Background(), testHash, buf, nil)
122         if err != nil {
123                 t.Fatalf("Error during GetBlock for %q: %s", testHash, err)
124         } else if bytes.Compare(buf[:size], testBlock) != 0 {
125                 t.Errorf("Get response incorrect. Expected %q; found %q", testBlock, buf[:size])
126         }
127 }