github.com/KYVENetwork/cometbft/v38@v38.0.3/libs/autofile/group_test.go (about) 1 package autofile 2 3 import ( 4 "io" 5 "os" 6 "path/filepath" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11 12 cmtos "github.com/KYVENetwork/cometbft/v38/libs/os" 13 cmtrand "github.com/KYVENetwork/cometbft/v38/libs/rand" 14 ) 15 16 func createTestGroupWithHeadSizeLimit(t *testing.T, headSizeLimit int64) *Group { 17 testID := cmtrand.Str(12) 18 testDir := "_test_" + testID 19 err := cmtos.EnsureDir(testDir, 0700) 20 require.NoError(t, err, "Error creating dir") 21 22 headPath := testDir + "/myfile" 23 g, err := OpenGroup(headPath, GroupHeadSizeLimit(headSizeLimit)) 24 require.NoError(t, err, "Error opening Group") 25 require.NotEqual(t, nil, g, "Failed to create Group") 26 27 return g 28 } 29 30 func destroyTestGroup(t *testing.T, g *Group) { 31 g.Close() 32 33 err := os.RemoveAll(g.Dir) 34 require.NoError(t, err, "Error removing test Group directory") 35 } 36 37 func assertGroupInfo(t *testing.T, gInfo GroupInfo, minIndex, maxIndex int, totalSize, headSize int64) { 38 assert.Equal(t, minIndex, gInfo.MinIndex) 39 assert.Equal(t, maxIndex, gInfo.MaxIndex) 40 assert.Equal(t, totalSize, gInfo.TotalSize) 41 assert.Equal(t, headSize, gInfo.HeadSize) 42 } 43 44 func TestCheckHeadSizeLimit(t *testing.T) { 45 g := createTestGroupWithHeadSizeLimit(t, 1000*1000) 46 47 // At first, there are no files. 48 assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 0, 0) 49 50 // Write 1000 bytes 999 times. 51 for i := 0; i < 999; i++ { 52 err := g.WriteLine(cmtrand.Str(999)) 53 require.NoError(t, err, "Error appending to head") 54 } 55 err := g.FlushAndSync() 56 require.NoError(t, err) 57 assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 999000, 999000) 58 59 // Even calling checkHeadSizeLimit manually won't rotate it. 60 g.checkHeadSizeLimit() 61 assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 999000, 999000) 62 63 // Write 1000 more bytes. 64 err = g.WriteLine(cmtrand.Str(999)) 65 require.NoError(t, err, "Error appending to head") 66 err = g.FlushAndSync() 67 require.NoError(t, err) 68 69 // Calling checkHeadSizeLimit this time rolls it. 70 g.checkHeadSizeLimit() 71 assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 1000000, 0) 72 73 // Write 1000 more bytes. 74 err = g.WriteLine(cmtrand.Str(999)) 75 require.NoError(t, err, "Error appending to head") 76 err = g.FlushAndSync() 77 require.NoError(t, err) 78 79 // Calling checkHeadSizeLimit does nothing. 80 g.checkHeadSizeLimit() 81 assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 1001000, 1000) 82 83 // Write 1000 bytes 999 times. 84 for i := 0; i < 999; i++ { 85 err = g.WriteLine(cmtrand.Str(999)) 86 require.NoError(t, err, "Error appending to head") 87 } 88 err = g.FlushAndSync() 89 require.NoError(t, err) 90 assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 2000000, 1000000) 91 92 // Calling checkHeadSizeLimit rolls it again. 93 g.checkHeadSizeLimit() 94 assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2000000, 0) 95 96 // Write 1000 more bytes. 97 _, err = g.Head.Write([]byte(cmtrand.Str(999) + "\n")) 98 require.NoError(t, err, "Error appending to head") 99 err = g.FlushAndSync() 100 require.NoError(t, err) 101 assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2001000, 1000) 102 103 // Calling checkHeadSizeLimit does nothing. 104 g.checkHeadSizeLimit() 105 assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2001000, 1000) 106 107 // Cleanup 108 destroyTestGroup(t, g) 109 } 110 111 func TestRotateFile(t *testing.T) { 112 g := createTestGroupWithHeadSizeLimit(t, 0) 113 114 // Create a different temporary directory and move into it, to make sure 115 // relative paths are resolved at Group creation 116 origDir, err := os.Getwd() 117 require.NoError(t, err) 118 defer func() { 119 if err := os.Chdir(origDir); err != nil { 120 t.Error(err) 121 } 122 }() 123 124 dir, err := os.MkdirTemp("", "rotate_test") 125 require.NoError(t, err) 126 defer os.RemoveAll(dir) 127 err = os.Chdir(dir) 128 require.NoError(t, err) 129 130 require.True(t, filepath.IsAbs(g.Head.Path)) 131 require.True(t, filepath.IsAbs(g.Dir)) 132 133 // Create and rotate files 134 err = g.WriteLine("Line 1") 135 require.NoError(t, err) 136 err = g.WriteLine("Line 2") 137 require.NoError(t, err) 138 err = g.WriteLine("Line 3") 139 require.NoError(t, err) 140 err = g.FlushAndSync() 141 require.NoError(t, err) 142 g.RotateFile() 143 err = g.WriteLine("Line 4") 144 require.NoError(t, err) 145 err = g.WriteLine("Line 5") 146 require.NoError(t, err) 147 err = g.WriteLine("Line 6") 148 require.NoError(t, err) 149 err = g.FlushAndSync() 150 require.NoError(t, err) 151 152 // Read g.Head.Path+"000" 153 body1, err := os.ReadFile(g.Head.Path + ".000") 154 assert.NoError(t, err, "Failed to read first rolled file") 155 if string(body1) != "Line 1\nLine 2\nLine 3\n" { 156 t.Errorf("got unexpected contents: [%v]", string(body1)) 157 } 158 159 // Read g.Head.Path 160 body2, err := os.ReadFile(g.Head.Path) 161 assert.NoError(t, err, "Failed to read first rolled file") 162 if string(body2) != "Line 4\nLine 5\nLine 6\n" { 163 t.Errorf("got unexpected contents: [%v]", string(body2)) 164 } 165 166 // Make sure there are no files in the current, temporary directory 167 files, err := os.ReadDir(".") 168 require.NoError(t, err) 169 assert.Empty(t, files) 170 171 // Cleanup 172 destroyTestGroup(t, g) 173 } 174 175 func TestWrite(t *testing.T) { 176 g := createTestGroupWithHeadSizeLimit(t, 0) 177 178 written := []byte("Medusa") 179 _, err := g.Write(written) 180 require.NoError(t, err) 181 err = g.FlushAndSync() 182 require.NoError(t, err) 183 184 read := make([]byte, len(written)) 185 gr, err := g.NewReader(0) 186 require.NoError(t, err, "failed to create reader") 187 188 _, err = gr.Read(read) 189 assert.NoError(t, err, "failed to read data") 190 assert.Equal(t, written, read) 191 192 // Cleanup 193 destroyTestGroup(t, g) 194 } 195 196 // test that Read reads the required amount of bytes from all the files in the 197 // group and returns no error if n == size of the given slice. 198 func TestGroupReaderRead(t *testing.T) { 199 g := createTestGroupWithHeadSizeLimit(t, 0) 200 201 professor := []byte("Professor Monster") 202 _, err := g.Write(professor) 203 require.NoError(t, err) 204 err = g.FlushAndSync() 205 require.NoError(t, err) 206 g.RotateFile() 207 frankenstein := []byte("Frankenstein's Monster") 208 _, err = g.Write(frankenstein) 209 require.NoError(t, err) 210 err = g.FlushAndSync() 211 require.NoError(t, err) 212 213 totalWrittenLength := len(professor) + len(frankenstein) 214 read := make([]byte, totalWrittenLength) 215 gr, err := g.NewReader(0) 216 require.NoError(t, err, "failed to create reader") 217 218 n, err := gr.Read(read) 219 assert.NoError(t, err, "failed to read data") 220 assert.Equal(t, totalWrittenLength, n, "not enough bytes read") 221 professorPlusFrankenstein := professor 222 professorPlusFrankenstein = append(professorPlusFrankenstein, frankenstein...) 223 assert.Equal(t, professorPlusFrankenstein, read) 224 225 // Cleanup 226 destroyTestGroup(t, g) 227 } 228 229 // test that Read returns an error if number of bytes read < size of 230 // the given slice. Subsequent call should return 0, io.EOF. 231 func TestGroupReaderRead2(t *testing.T) { 232 g := createTestGroupWithHeadSizeLimit(t, 0) 233 234 professor := []byte("Professor Monster") 235 _, err := g.Write(professor) 236 require.NoError(t, err) 237 err = g.FlushAndSync() 238 require.NoError(t, err) 239 g.RotateFile() 240 frankenstein := []byte("Frankenstein's Monster") 241 frankensteinPart := []byte("Frankenstein") 242 _, err = g.Write(frankensteinPart) // note writing only a part 243 require.NoError(t, err) 244 err = g.FlushAndSync() 245 require.NoError(t, err) 246 247 totalLength := len(professor) + len(frankenstein) 248 read := make([]byte, totalLength) 249 gr, err := g.NewReader(0) 250 require.NoError(t, err, "failed to create reader") 251 252 // 1) n < (size of the given slice), io.EOF 253 n, err := gr.Read(read) 254 assert.Equal(t, io.EOF, err) 255 assert.Equal(t, len(professor)+len(frankensteinPart), n, "Read more/less bytes than it is in the group") 256 257 // 2) 0, io.EOF 258 n, err = gr.Read([]byte("0")) 259 assert.Equal(t, io.EOF, err) 260 assert.Equal(t, 0, n) 261 262 // Cleanup 263 destroyTestGroup(t, g) 264 } 265 266 func TestMinIndex(t *testing.T) { 267 g := createTestGroupWithHeadSizeLimit(t, 0) 268 269 assert.Zero(t, g.MinIndex(), "MinIndex should be zero at the beginning") 270 271 // Cleanup 272 destroyTestGroup(t, g) 273 } 274 275 func TestMaxIndex(t *testing.T) { 276 g := createTestGroupWithHeadSizeLimit(t, 0) 277 278 assert.Zero(t, g.MaxIndex(), "MaxIndex should be zero at the beginning") 279 280 err := g.WriteLine("Line 1") 281 require.NoError(t, err) 282 err = g.FlushAndSync() 283 require.NoError(t, err) 284 g.RotateFile() 285 286 assert.Equal(t, 1, g.MaxIndex(), "MaxIndex should point to the last file") 287 288 // Cleanup 289 destroyTestGroup(t, g) 290 }