github.com/bhojpur/cache@v0.0.4/pkg/temporal/temporal_test.go (about)

     1  package temporal_test
     2  
     3  // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved.
     4  
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  
    12  // The above copyright notice and this permission notice shall be included in
    13  // all copies or substantial portions of the Software.
    14  
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    21  // THE SOFTWARE.
    22  
    23  import (
    24  	"bytes"
    25  	"fmt"
    26  	"log"
    27  	"os"
    28  	"testing"
    29  	"time"
    30  
    31  	temporal "github.com/bhojpur/cache/pkg/temporal"
    32  )
    33  
    34  func setupDB(dbName string) (temporal.Database, string, error) {
    35  	temp_file := fmt.Sprintf("test_temp_%s", dbName)
    36  	config := temporal.MemCacheConfig{Path: temp_file}
    37  
    38  	db, err := temporal.Open(config)
    39  	if err != nil {
    40  		return nil, temp_file, err
    41  	}
    42  	return db, temp_file, nil
    43  }
    44  
    45  func clean(db temporal.Database, temp_filepath string) {
    46  	db.Close()
    47  	err := os.Remove(temp_filepath)
    48  	if err != nil {
    49  		fmt.Println(err.Error())
    50  	}
    51  }
    52  
    53  func TestTsdb_Add(t *testing.T) {
    54  	tname := "TestTsdb_Add"
    55  	db, filePath, err := setupDB(tname)
    56  	if err != nil {
    57  		t.Fatal(err.Error())
    58  	}
    59  
    60  	defer clean(db, filePath)
    61  	series := createDummyRecords(100, false)
    62  	err = db.Add(tname, series)
    63  
    64  	if err != nil {
    65  		t.Fatal(err)
    66  	}
    67  
    68  	query := temporal.Query{Series: tname, MaxEntries: 100, From: series[0].Time, To: series[len(series)-1].Time, Sort: temporal.ASC}
    69  	resSeries, nextEntry, err := db.Query(query)
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  
    74  	if compareTimeSeries(resSeries, series) == false {
    75  		t.Error("Inserted and Fetched did not match")
    76  	}
    77  	if nextEntry != nil {
    78  		t.Error("nextEntry is null")
    79  	}
    80  }
    81  
    82  func TestMemCacheDB_Create(t *testing.T) {
    83  	tname := "TestMemCacheDB_Create"
    84  	db, filePath, err := setupDB(tname)
    85  	if err != nil {
    86  		t.Fatal(err.Error())
    87  	}
    88  	defer clean(db, filePath)
    89  
    90  	err = db.Create(tname)
    91  	if err != nil {
    92  		t.Error(err.Error())
    93  	}
    94  
    95  	err = db.Create(tname)
    96  	if err == nil {
    97  		t.Error("Duplicate Buckets are allowed")
    98  	}
    99  }
   100  func TestTsdb_CheckDescending(t *testing.T) {
   101  	tname := "TestTsdb_CheckDescending"
   102  	db, filePath, err := setupDB(tname)
   103  	if err != nil {
   104  		t.Fatal(err.Error())
   105  	}
   106  
   107  	defer clean(db, filePath)
   108  	series := createDummyRecords(100, false)
   109  	err = db.Add(tname, series)
   110  
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  
   115  	query := temporal.Query{Series: tname, MaxEntries: 100, From: series[0].Time, To: series[len(series)-1].Time, Sort: temporal.DESC}
   116  	resSeries, nextEntry, err := db.Query(query)
   117  	if err != nil {
   118  		t.Fatal(err)
   119  	}
   120  
   121  	// reverse the Series
   122  	for i := len(series)/2 - 1; i >= 0; i-- {
   123  		opp := len(series) - 1 - i
   124  		series[i], series[opp] = series[opp], series[i]
   125  	}
   126  	if compareTimeSeries(resSeries, series) == false {
   127  		t.Error("Inserted and Fetched did not match")
   128  	}
   129  	if nextEntry != nil {
   130  		t.Error("nextEntry is null")
   131  	}
   132  }
   133  
   134  func TestTsdb_QueryPagination(t *testing.T) {
   135  	tname := "TestTsdb_CheckDescending"
   136  	db, filePath, err := setupDB(tname)
   137  	if err != nil {
   138  		t.Fatal(err.Error())
   139  	}
   140  
   141  	defer clean(db, filePath)
   142  	series := createDummyRecords(100, false)
   143  	err = db.Add(tname, series)
   144  
   145  	query := temporal.Query{Series: tname, MaxEntries: 50, From: series[0].Time, To: series[len(series)-1].Time, Sort: temporal.ASC}
   146  	resSeries, nextEntry, err := db.Query(query)
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  
   151  	if compareTimeSeries(resSeries, series[0:50]) == false {
   152  		t.Error("First page entries did not match")
   153  	}
   154  
   155  	if nextEntry == nil {
   156  		t.Error("nextEntry is null")
   157  	}
   158  	query = temporal.Query{Series: tname, MaxEntries: 50, From: *nextEntry, To: series[len(series)-1].Time, Sort: temporal.ASC}
   159  	resSeries, nextEntry, err = db.Query(query)
   160  	if err != nil {
   161  		t.Fatal(err)
   162  	}
   163  
   164  	if compareTimeSeries(resSeries, series[50:100]) == false {
   165  		t.Error("Second page entries did not match")
   166  	}
   167  
   168  	if nextEntry != nil {
   169  		t.Error("nextEntry is null")
   170  	}
   171  }
   172  
   173  func TestTsdb_QueryPages(t *testing.T) {
   174  	tname := "TestTsdb_CheckDescending"
   175  	limit := 25
   176  	count := 100
   177  	db, filePath, err := setupDB(tname)
   178  	if err != nil {
   179  		t.Fatal(err.Error())
   180  	}
   181  
   182  	defer clean(db, filePath)
   183  	series := createDummyRecords(count, false)
   184  	err = db.Add(tname, series)
   185  
   186  	query := temporal.Query{Series: tname, MaxEntries: limit, From: series[0].Time, To: series[len(series)-1].Time, Sort: temporal.ASC}
   187  	pages, retCount, err := db.GetPages(query)
   188  	if err != nil {
   189  		t.Fatal(err)
   190  	}
   191  
   192  	if retCount != count {
   193  		t.Error("Length of time series do not match")
   194  	}
   195  
   196  	if len(pages) != count/limit {
   197  		t.Error("Number of pages is not as expected")
   198  	}
   199  
   200  	for i := 0; i < count/limit; i = i + 1 {
   201  		if pages[i] != series[i*limit].Time {
   202  			t.Errorf("Page indices are not matching %v != %v", pages[i], series[i*limit].Time)
   203  		}
   204  	}
   205  }
   206  
   207  func compareTimeSeries(s1, s2 temporal.TimeSeries) bool {
   208  	if len(s1) != len(s2) {
   209  		return false
   210  	}
   211  	for i := 0; i < len(s1); i++ {
   212  		e1 := s1[i]
   213  		e2 := s2[i]
   214  		if e1.Time != e2.Time || bytes.Compare(e1.Value, e2.Value) != 0 {
   215  			return false
   216  		}
   217  	}
   218  
   219  	return true
   220  }
   221  
   222  func createDummyRecords(count int, dec bool) temporal.TimeSeries {
   223  	timeVal := time.Date(
   224  		2009, 0, 0, 0, 0, 0, 0, time.UTC)
   225  
   226  	timeseries := make(temporal.TimeSeries, 0, count)
   227  
   228  	for i := 0; i < count; i++ {
   229  		value, err := timeVal.MarshalBinary()
   230  		if err != nil {
   231  			log.Fatal(err)
   232  			return nil
   233  		}
   234  		timeseries = append(timeseries, temporal.TimeEntry{timeVal.UnixNano(), value})
   235  		if dec {
   236  			timeVal = timeVal.Add(-time.Second)
   237  		} else {
   238  			timeVal = timeVal.Add(time.Second)
   239  		}
   240  	}
   241  	return timeseries
   242  }