test: proper unit tests
This commit is contained in:
parent
3d7e1dd077
commit
9a84b801c9
|
@ -39,11 +39,26 @@ func GetJokeById(ctx context.Context, bucket *minio.Client, cache *redis.Client,
|
|||
}
|
||||
|
||||
if err == nil {
|
||||
return jokeFromMemory, "", nil
|
||||
contentTypeFromMemory, err := memory.Get("id:" + strconv.Itoa(id) + ":content-type")
|
||||
if err != nil && !errors.Is(err, bigcache.ErrEntryNotFound) {
|
||||
return []byte{}, "", fmt.Errorf("acquiring joke content type from memory: %w", err)
|
||||
}
|
||||
|
||||
return jokeFromMemory, string(contentTypeFromMemory), nil
|
||||
}
|
||||
|
||||
jokeFromCache, err := cache.Get(ctx, "jokes:id:"+strconv.Itoa(id)).Result()
|
||||
if err != nil {
|
||||
if err != nil && !errors.Is(err, redis.Nil) {
|
||||
return []byte{}, "", fmt.Errorf("acquiring joke from cache: %w", err)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
// Get content type
|
||||
contentTypeFromCache, err := cache.Get(ctx, "jokes:id:"+strconv.Itoa(id)+":content-type").Result()
|
||||
if err != nil && !errors.Is(err, redis.Nil) {
|
||||
return []byte{}, "", fmt.Errorf("acquiring content type from cache: %w", err)
|
||||
}
|
||||
|
||||
// Decode hex string to bytes
|
||||
imageBytes, err := hex.DecodeString(jokeFromCache)
|
||||
if err != nil {
|
||||
|
@ -55,9 +70,14 @@ func GetJokeById(ctx context.Context, bucket *minio.Client, cache *redis.Client,
|
|||
if err != nil {
|
||||
log.Printf("setting memory cache: %s", err.Error())
|
||||
}
|
||||
|
||||
err = memory.Set("id:"+strconv.Itoa(id)+":content-type", []byte(contentTypeFromCache))
|
||||
if err != nil {
|
||||
log.Printf("setting memory cache: %s", err.Error())
|
||||
}
|
||||
}(id, imageBytes)
|
||||
|
||||
return imageBytes, "", nil
|
||||
return imageBytes, contentTypeFromCache, nil
|
||||
}
|
||||
|
||||
jokes, err := ListJokesFromBucket(ctx, bucket, cache)
|
||||
|
@ -91,6 +111,11 @@ func GetJokeById(ctx context.Context, bucket *minio.Client, cache *redis.Client,
|
|||
if err != nil {
|
||||
log.Printf("setting cache: %s", err.Error())
|
||||
}
|
||||
|
||||
err = cache.Set(ctx, "jokes:id:"+strconv.Itoa(id)+":content-type", jokes[id].ContentType, time.Hour*1).Err()
|
||||
if err != nil {
|
||||
log.Printf("setting cache: %s", err.Error())
|
||||
}
|
||||
}(id, image)
|
||||
|
||||
return image, jokes[id].ContentType, nil
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package joke_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"jokes-bapak2-api/core/joke"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGetRandomJoke(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
image, contentType, err := joke.GetRandomJoke(ctx, bucket, cache, memory)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if contentType != "image/jpeg" {
|
||||
t.Errorf("expecting contentType of 'image/jpeg', instead got %s", contentType)
|
||||
}
|
||||
|
||||
if len(image) == 0 {
|
||||
t.Error("empty image")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetJokeById(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
image, contentType, err := joke.GetJokeById(ctx, bucket, cache, memory, 0)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if contentType != "image/jpeg" {
|
||||
t.Errorf("expecting contentType of 'image/jpeg', instead got %s", contentType)
|
||||
}
|
||||
|
||||
if len(image) == 0 {
|
||||
t.Error("empty image")
|
||||
}
|
||||
|
||||
cachedImage, cachedContentType, err := joke.GetJokeById(ctx, bucket, cache, memory, 0)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if cachedContentType != contentType {
|
||||
t.Errorf("difference in contentType: original %s vs cached %s", contentType, cachedContentType)
|
||||
}
|
||||
|
||||
if string(cachedImage) != string(image) {
|
||||
t.Errorf("difference in image bytes")
|
||||
}
|
||||
|
||||
cachedImage2, cachedContentType2, err := joke.GetJokeById(ctx, bucket, cache, memory, 0)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if cachedContentType2 != contentType {
|
||||
t.Errorf("difference in contentType: original %s vs cached %s", contentType, cachedContentType2)
|
||||
}
|
||||
|
||||
if string(cachedImage2) != string(image) {
|
||||
t.Errorf("difference in image bytes")
|
||||
}
|
||||
|
||||
}
|
|
@ -2,6 +2,7 @@ package joke_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
@ -61,12 +62,22 @@ func TestMain(m *testing.M) {
|
|||
memoryInstance, err := bigcache.NewBigCache(bigcache.DefaultConfig(time.Second * 30))
|
||||
if err != nil {
|
||||
log.Fatalf("creating bigcache client: %s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
bucket = minioClient
|
||||
cache = redisClient
|
||||
memory = memoryInstance
|
||||
|
||||
setupCtx, setupCancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
defer setupCancel()
|
||||
|
||||
err = setupBucketStorage(setupCtx, minioClient)
|
||||
if err != nil {
|
||||
log.Fatalf("set up bucket storage: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
exitCode := m.Run()
|
||||
|
||||
cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), time.Minute)
|
||||
|
@ -77,7 +88,12 @@ func TestMain(m *testing.M) {
|
|||
log.Printf("flushing redis: %s", err.Error())
|
||||
}
|
||||
|
||||
err = cache.Close()
|
||||
err = minioClient.RemoveBucketWithOptions(cleanupCtx, "jokesbapak2", minio.RemoveBucketOptions{ForceDelete: true})
|
||||
if err != nil {
|
||||
log.Printf("removing bucket: %s", err.Error())
|
||||
}
|
||||
|
||||
err = memoryInstance.Close()
|
||||
if err != nil {
|
||||
log.Printf("closing cache client: %s", err.Error())
|
||||
}
|
||||
|
@ -89,3 +105,52 @@ func TestMain(m *testing.M) {
|
|||
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func setupBucketStorage(ctx context.Context, minioClient *minio.Client) error {
|
||||
bucketFound, err := minioClient.BucketExists(ctx, "jokesbapak2")
|
||||
if err != nil {
|
||||
return fmt.Errorf("checking MinIO bucket: %w", err)
|
||||
}
|
||||
|
||||
if !bucketFound {
|
||||
err = minioClient.MakeBucket(ctx, "jokesbapak2", minio.MakeBucketOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating MinIO bucket: %w", err)
|
||||
}
|
||||
|
||||
policy := `{
|
||||
"Version":"2012-10-17",
|
||||
"Statement":[
|
||||
{
|
||||
"Sid": "AddPerm",
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action":["s3:GetObject"],
|
||||
"Resource":["arn:aws:s3:::jokesbapak2/*"]
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
err = minioClient.SetBucketPolicy(ctx, "jokesbapak2", policy)
|
||||
if err != nil {
|
||||
return fmt.Errorf("setting bucket policy: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
sampleFiles := []string{
|
||||
"../../samples/sample1.jpg",
|
||||
"../../samples/sample2.jpg",
|
||||
"../../samples/sample3.jpg",
|
||||
"../../samples/sample4.jpg",
|
||||
"../../samples/sample5.jpg",
|
||||
}
|
||||
|
||||
for i, file := range sampleFiles {
|
||||
_, err := minioClient.FPutObject(ctx, "jokesbapak2", fmt.Sprintf("sample%d.jpg", i), file, minio.PutObjectOptions{ContentType: "image/jpeg"})
|
||||
if err != nil {
|
||||
return fmt.Errorf("putting object: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -37,8 +37,18 @@ func ListJokesFromBucket(ctx context.Context, bucket *minio.Client, cache *redis
|
|||
return []Joke{}, fmt.Errorf("enumerating objects: %w", object.Err)
|
||||
}
|
||||
|
||||
var contentType = object.ContentType
|
||||
|
||||
if contentType == "" {
|
||||
stat, err := bucket.StatObject(ctx, JokesBapak2Bucket, object.Key, minio.StatObjectOptions{})
|
||||
if err != nil {
|
||||
return []Joke{}, fmt.Errorf("stat object: %w", err)
|
||||
}
|
||||
|
||||
contentType = stat.ContentType
|
||||
}
|
||||
if !object.IsDeleteMarker {
|
||||
jokes = append(jokes, Joke{ModifiedAt: object.Restore.ExpiryTime, FileName: object.Key, ContentType: object.ContentType})
|
||||
jokes = append(jokes, Joke{ModifiedAt: object.LastModified, FileName: object.Key, ContentType: contentType})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package joke_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"jokes-bapak2-api/core/joke"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestListJokeFromBucket(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
jokes, err := joke.ListJokesFromBucket(ctx, bucket, cache)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if len(jokes) != 5 {
|
||||
t.Errorf("expected joke to have a length of 5, instead got %d", len(jokes))
|
||||
}
|
||||
}
|
|
@ -37,6 +37,12 @@ func GetTodaysJoke(ctx context.Context, bucket *minio.Client, cache *redis.Clien
|
|||
}
|
||||
|
||||
if err == nil {
|
||||
// Get content type
|
||||
contentTypeFromCache, err := cache.Get(ctx, "jokes:today:"+today+":content-type").Result()
|
||||
if err != nil && !errors.Is(err, redis.Nil) {
|
||||
return []byte{}, "", fmt.Errorf("acquiring content type from cache: %w", err)
|
||||
}
|
||||
|
||||
// Decode hex string to bytes
|
||||
imageBytes, err := hex.DecodeString(jokeFromCache)
|
||||
if err != nil {
|
||||
|
@ -48,9 +54,14 @@ func GetTodaysJoke(ctx context.Context, bucket *minio.Client, cache *redis.Clien
|
|||
if err != nil {
|
||||
log.Printf("setting memory cache: %s", err.Error())
|
||||
}
|
||||
|
||||
err = memory.Set("today:"+today+":content-type", []byte(contentTypeFromCache))
|
||||
if err != nil {
|
||||
log.Printf("setting memory cache: %s", err.Error())
|
||||
}
|
||||
}(today, imageBytes)
|
||||
|
||||
return imageBytes, "", nil
|
||||
return imageBytes, contentTypeFromCache, nil
|
||||
}
|
||||
|
||||
// If everything not exists, we get a new random joke
|
||||
|
@ -70,6 +81,11 @@ func GetTodaysJoke(ctx context.Context, bucket *minio.Client, cache *redis.Clien
|
|||
if err != nil {
|
||||
log.Printf("setting today cache to redis: %s", err.Error())
|
||||
}
|
||||
|
||||
err = cache.Set(ctx, "jokes:today:"+today+":content-type", contentType, time.Hour*24).Err()
|
||||
if err != nil {
|
||||
log.Printf("setting today cache to redis: %s", err.Error())
|
||||
}
|
||||
}(today, randomJoke)
|
||||
|
||||
return randomJoke, contentType, nil
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package joke_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"jokes-bapak2-api/core/joke"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGetTodaysJoke(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
image, contentType, err := joke.GetTodaysJoke(ctx, bucket, cache, memory)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if contentType != "image/jpeg" {
|
||||
t.Errorf("expecting contentType of 'image/jpeg', instead got %s", contentType)
|
||||
}
|
||||
|
||||
if len(image) == 0 {
|
||||
t.Errorf("empty image")
|
||||
}
|
||||
|
||||
cachedImage, cachedContentType, err := joke.GetTodaysJoke(ctx, bucket, cache, memory)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if contentType != cachedContentType {
|
||||
t.Errorf("difference on contentType: original %s vs cached %s", contentType, cachedContentType)
|
||||
}
|
||||
|
||||
if string(image) != string(cachedImage) {
|
||||
t.Errorf("difference in image")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package joke_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"jokes-bapak2-api/core/joke"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGetTotalJoke(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
|
||||
defer cancel()
|
||||
|
||||
total, err := joke.GetTotalJoke(ctx, bucket, cache, memory)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if total != 5 {
|
||||
t.Errorf("expecting total to be 5 instead got %d", total)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue