Skip to main content
Glama

Storyden

by Southclaws
profile_cache_test.go6.3 kB
package account_test import ( "context" "net/http" "testing" "time" "github.com/google/uuid" "github.com/rs/xid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/fx" "github.com/Southclaws/storyden/app/resources/account/account_writer" "github.com/Southclaws/storyden/app/resources/profile/profile_cache" "github.com/Southclaws/storyden/app/transports/http/openapi" "github.com/Southclaws/storyden/internal/infrastructure/cache" "github.com/Southclaws/storyden/internal/integration" "github.com/Southclaws/storyden/internal/integration/e2e" "github.com/Southclaws/storyden/tests" ) func TestAccountCacheWithEmailOperations(t *testing.T) { t.Parallel() integration.Test(t, nil, e2e.Setup(), fx.Invoke(func( lc fx.Lifecycle, root context.Context, cl *openapi.ClientWithResponses, sh *e2e.SessionHelper, aw *account_writer.Writer, cacheStore cache.Store, profileCache *profile_cache.Cache, ) { lc.Append(fx.StartHook(func() { r := require.New(t) a := assert.New(t) email1 := xid.New().String() + "first@example.com" password := "password" signup, err := cl.AuthEmailPasswordSignupWithResponse(root, nil, openapi.AuthEmailPasswordSignupJSONRequestBody{ Email: email1, Password: password, }) tests.Ok(t, err, signup) session := e2e.WithSessionFromHeader(t, root, signup.HTTPResponse.Header) accountID := openapi.ParseID(signup.JSON200.Id) accGet1, err := cl.AccountGetWithResponse(root, session) tests.Ok(t, err, accGet1) a.Len(accGet1.JSON200.EmailAddresses, 1, "account should have one email") lastModified1Header := accGet1.HTTPResponse.Header.Get("Last-Modified") r.NotEmpty(lastModified1Header, "Last-Modified header should be present") cacheKey := "profile:last-modified:" + accountID.String() email2 := xid.New().String() + "second@example.com" addEmail, err := cl.AccountEmailAddWithResponse(root, openapi.AccountEmailInitialProps{ EmailAddress: email2, }, session) tests.Ok(t, err, addEmail) // Wait for the cache update event to be processed via pubsub time.Sleep(200 * time.Millisecond) // Now cache should be populated with timestamp from when email was added cachedValue1, err := cacheStore.Get(root, cacheKey) r.NoError(err, "cache value should exist after email added") cachedTime1, err := time.Parse(time.RFC3339Nano, cachedValue1) r.NoError(err, "cached time should be parseable") accGet2, err := cl.AccountGetWithResponse(root, session) tests.Ok(t, err, accGet2) r.Len(accGet2.JSON200.EmailAddresses, 2, "account should now have two emails") lastModified2Header := accGet2.HTTPResponse.Header.Get("Last-Modified") r.NotEmpty(lastModified2Header, "Last-Modified header should be present") // Make a conditional request - should return 304 since cache timestamp is newer accGet304, err := cl.AccountGetWithResponse(root, session, func(ctx context.Context, req *http.Request) error { req.Header.Set("If-Modified-Since", lastModified2Header) return nil }) tests.Status(t, err, accGet304, 304) // Remove the email removeEmail, err := cl.AccountEmailRemoveWithResponse(root, addEmail.JSON200.Id, session) tests.Ok(t, err, removeEmail) // Wait for the cache update event to be processed time.Sleep(200 * time.Millisecond) // Cache should be updated with a newer timestamp cachedValue2, err := cacheStore.Get(root, cacheKey) r.NoError(err, "cache value should still exist after email removed") cachedTime2, err := time.Parse(time.RFC3339Nano, cachedValue2) r.NoError(err, "cached time should be parseable") a.True(cachedTime2.After(cachedTime1), "cached time should be updated after removing email (cachedTime1: %v, cachedTime2: %v)", cachedTime1, cachedTime2) accGet3, err := cl.AccountGetWithResponse(root, session) tests.Ok(t, err, accGet3) r.Len(accGet3.JSON200.EmailAddresses, 1, "account should be back to one email") })) })) } func TestAccountCacheWithProfileUpdate(t *testing.T) { t.Parallel() integration.Test(t, nil, e2e.Setup(), fx.Invoke(func( lc fx.Lifecycle, root context.Context, cl *openapi.ClientWithResponses, sh *e2e.SessionHelper, aw *account_writer.Writer, cacheStore cache.Store, profileCache *profile_cache.Cache, ) { lc.Append(fx.StartHook(func() { r := require.New(t) a := assert.New(t) email := uuid.NewString() + "@example.com" password := "password" signup, err := cl.AuthEmailPasswordSignupWithResponse(root, nil, openapi.AuthEmailPasswordSignupJSONRequestBody{ Email: email, Password: password, }) tests.Ok(t, err, signup) session := e2e.WithSessionFromHeader(t, root, signup.HTTPResponse.Header) accountID := openapi.ParseID(signup.JSON200.Id) cacheKey := "profile:last-modified:" + accountID.String() // Update the profile newBio := "This is my new bio" updateResp, err := cl.AccountUpdateWithResponse(root, openapi.AccountMutableProps{ Bio: &newBio, }, session) tests.Ok(t, err, updateResp) // Wait for the cache update event to be processed time.Sleep(200 * time.Millisecond) // Cache should now be populated cachedValue1, err := cacheStore.Get(root, cacheKey) r.NoError(err, "cache value should exist after profile update") cachedTime1, err := time.Parse(time.RFC3339Nano, cachedValue1) r.NoError(err, "cached time should be parseable") accGet2, err := cl.AccountGetWithResponse(root, session) tests.Ok(t, err, accGet2) a.Contains(accGet2.JSON200.Bio, newBio, "bio should contain the updated text") // Update again newBio2 := "This is my second bio" updateResp2, err := cl.AccountUpdateWithResponse(root, openapi.AccountMutableProps{ Bio: &newBio2, }, session) tests.Ok(t, err, updateResp2) time.Sleep(200 * time.Millisecond) cachedValue2, err := cacheStore.Get(root, cacheKey) r.NoError(err, "cache value should still exist after second profile update") cachedTime2, err := time.Parse(time.RFC3339Nano, cachedValue2) r.NoError(err, "cached time should be parseable") a.True(cachedTime2.After(cachedTime1), "cached time should be updated after profile update (cachedTime1: %v, cachedTime2: %v)", cachedTime1, cachedTime2) })) })) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Southclaws/storyden'

If you have feedback or need assistance with the MCP directory API, please join our Discord server