test: proper unit tests

This commit is contained in:
Reinaldy Rafli 2022-09-03 20:13:04 +07:00
parent 3d7e1dd077
commit 9a84b801c9
No known key found for this signature in database
GPG Key ID: EA1B2522C1693EC8
8 changed files with 276 additions and 6 deletions

View File

@ -39,11 +39,26 @@ func GetJokeById(ctx context.Context, bucket *minio.Client, cache *redis.Client,
} }
if err == nil { 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() 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 // Decode hex string to bytes
imageBytes, err := hex.DecodeString(jokeFromCache) imageBytes, err := hex.DecodeString(jokeFromCache)
if err != nil { if err != nil {
@ -55,9 +70,14 @@ func GetJokeById(ctx context.Context, bucket *minio.Client, cache *redis.Client,
if err != nil { if err != nil {
log.Printf("setting memory cache: %s", err.Error()) 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) }(id, imageBytes)
return imageBytes, "", nil return imageBytes, contentTypeFromCache, nil
} }
jokes, err := ListJokesFromBucket(ctx, bucket, cache) jokes, err := ListJokesFromBucket(ctx, bucket, cache)
@ -91,6 +111,11 @@ func GetJokeById(ctx context.Context, bucket *minio.Client, cache *redis.Client,
if err != nil { if err != nil {
log.Printf("setting cache: %s", err.Error()) 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) }(id, image)
return image, jokes[id].ContentType, nil return image, jokes[id].ContentType, nil

View File

@ -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")
}
}

View File

@ -2,6 +2,7 @@ package joke_test
import ( import (
"context" "context"
"fmt"
"log" "log"
"os" "os"
"testing" "testing"
@ -61,12 +62,22 @@ func TestMain(m *testing.M) {
memoryInstance, err := bigcache.NewBigCache(bigcache.DefaultConfig(time.Second * 30)) memoryInstance, err := bigcache.NewBigCache(bigcache.DefaultConfig(time.Second * 30))
if err != nil { if err != nil {
log.Fatalf("creating bigcache client: %s", err.Error()) log.Fatalf("creating bigcache client: %s", err.Error())
return
} }
bucket = minioClient bucket = minioClient
cache = redisClient cache = redisClient
memory = memoryInstance 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() exitCode := m.Run()
cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), time.Minute) cleanupCtx, cleanupCancel := context.WithTimeout(context.Background(), time.Minute)
@ -77,7 +88,12 @@ func TestMain(m *testing.M) {
log.Printf("flushing redis: %s", err.Error()) 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 { if err != nil {
log.Printf("closing cache client: %s", err.Error()) log.Printf("closing cache client: %s", err.Error())
} }
@ -89,3 +105,52 @@ func TestMain(m *testing.M) {
os.Exit(exitCode) 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
}

View File

@ -37,8 +37,18 @@ func ListJokesFromBucket(ctx context.Context, bucket *minio.Client, cache *redis
return []Joke{}, fmt.Errorf("enumerating objects: %w", object.Err) 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 { 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})
} }
} }

View File

@ -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))
}
}

View File

@ -37,6 +37,12 @@ func GetTodaysJoke(ctx context.Context, bucket *minio.Client, cache *redis.Clien
} }
if err == nil { 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 // Decode hex string to bytes
imageBytes, err := hex.DecodeString(jokeFromCache) imageBytes, err := hex.DecodeString(jokeFromCache)
if err != nil { if err != nil {
@ -48,9 +54,14 @@ func GetTodaysJoke(ctx context.Context, bucket *minio.Client, cache *redis.Clien
if err != nil { if err != nil {
log.Printf("setting memory cache: %s", err.Error()) 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) }(today, imageBytes)
return imageBytes, "", nil return imageBytes, contentTypeFromCache, nil
} }
// If everything not exists, we get a new random joke // 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 { if err != nil {
log.Printf("setting today cache to redis: %s", err.Error()) 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) }(today, randomJoke)
return randomJoke, contentType, nil return randomJoke, contentType, nil

View File

@ -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")
}
}

View File

@ -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)
}
}