mirror of
https://github.com/marcusDenslow/terminalShop.git
synced 2026-10-25 02:52:20 +02:00
Compare commits
3 Commits
viewport
...
Default-ca
| Author | SHA1 | Date | |
|---|---|---|---|
| 93cc69d028 | |||
| c7fa292730 | |||
| 0146fa3e76 |
@@ -1,5 +1,7 @@
|
||||
# Terminal Coffee Shop
|
||||
|
||||
this file was generated by Claude Code very early in development, it is not up to date AT ALL. It is also very wrong about the stack i ended up using. I plan to come back to write this myself but this will do for now until i get my first working version of this app.
|
||||
|
||||
A terminal-based e-commerce application for ordering coffee, built with Go and Bubbletea TUI framework. Inspired by [terminal.shop](https://terminal.shop).
|
||||
|
||||
## Current Status
|
||||
|
||||
201
api/handlers/addresses_test.go
Normal file
201
api/handlers/addresses_test.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"terminalShop/pkg/database"
|
||||
"terminalShop/pkg/models"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func setupAddressTestDB(t *testing.T) (string, models.User) {
|
||||
t.Helper()
|
||||
testDB := "test_addresses.db"
|
||||
os.Remove(testDB)
|
||||
database.ResetForTesting()
|
||||
|
||||
db, err := database.Connect(testDB)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to connect to test database: %v", err)
|
||||
}
|
||||
if err := database.Migrate(db); err != nil {
|
||||
t.Fatalf("failed to migrate: %v", err)
|
||||
}
|
||||
if err := database.Seed(db); err != nil {
|
||||
t.Fatalf("failed to seed: %v", err)
|
||||
}
|
||||
|
||||
user := models.User{
|
||||
SSHKeyFingerprint: "SHA256:addresstestfingerprint",
|
||||
SSHPublicKey: "ssh-ed25519 AAAA addresstestkey",
|
||||
Name: "Address Test User",
|
||||
Email: "address@example.com",
|
||||
}
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
t.Fatalf("failed to create test user: %v", err)
|
||||
}
|
||||
|
||||
return testDB, user
|
||||
}
|
||||
|
||||
func TestGetAddressesEmpty(t *testing.T) {
|
||||
testDB, user := setupAddressTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer database.ResetForTesting()
|
||||
|
||||
handler := NewAddressHandler("", "", "")
|
||||
|
||||
req := authRequest("GET", "/api/v1/addresses", nil, user.ID)
|
||||
w := httptest.NewRecorder()
|
||||
handler.GetAddresses(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected status 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
Addresses []interface{} `json:"addresses"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
|
||||
t.Fatalf("failed to decode response: %v", err)
|
||||
}
|
||||
if !resp.Success {
|
||||
t.Errorf("expexted success to be true")
|
||||
}
|
||||
if len(resp.Data.Addresses) != 0 {
|
||||
t.Errorf("expected + addresses, got %d", len(resp.Data.Addresses))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddressesIsolation(t *testing.T) {
|
||||
testDB, user1 := setupAddressTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
user2 := models.User{
|
||||
SSHKeyFingerprint: "SHA256:otheraddressuser",
|
||||
SSHPublicKey: "ssh-ed25519 AAAA otheraddressuser",
|
||||
}
|
||||
db.Create(&user2)
|
||||
|
||||
db.Create(&models.Address{
|
||||
UserID: user1.ID, Name: "User1 Addr", Street: "111 St",
|
||||
City: "Portland", State: "OR", Zip: "97201", Country: "US",
|
||||
})
|
||||
db.Create(&models.Address{
|
||||
UserID: user2.ID, Name: "User2 Addr", Street: "222 St",
|
||||
City: "Seattle", State: "WA", Zip: "98101", Country: "US",
|
||||
})
|
||||
|
||||
handler := NewAddressHandler("", "", "")
|
||||
|
||||
req := authRequest("GET", "/api/v1/addresses", nil, user1.ID)
|
||||
w := httptest.NewRecorder()
|
||||
handler.GetAddresses(w, req)
|
||||
|
||||
var resp struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
Addresses []models.Address `json:"addresses"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.NewDecoder(w.Body).Decode(&resp)
|
||||
|
||||
if len(resp.Data.Addresses) != 1 {
|
||||
t.Fatalf("expexted 1 address for user1, got %d", len(resp.Data.Addresses))
|
||||
}
|
||||
if resp.Data.Addresses[0].Name != "User Addr" {
|
||||
t.Errorf("expected 'User1 Addr', got %q", resp.Data.Addresses[0].Name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddressesWithData(t *testing.T) {
|
||||
testDB, user := setupAddressTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
|
||||
address1 := &models.Address{
|
||||
UserID: user.ID, Name: "Home", Street: "123 Main St",
|
||||
City: "Portland", State: "OR", Zip: "97201", Country: "US",
|
||||
}
|
||||
|
||||
address2 := &models.Address{
|
||||
UserID: user.ID, Name: "Work", Street: "456 Office Ave",
|
||||
City: "Portland", State: "OR", Zip: "97202", Country: "US",
|
||||
}
|
||||
|
||||
db.Create(address1)
|
||||
db.Create(address2)
|
||||
|
||||
handler := NewAddressHandler("", "", "")
|
||||
|
||||
req := authRequest("GET", "/api/v1/addresses", nil, user.ID)
|
||||
w := httptest.NewRecorder()
|
||||
handler.GetAddresses(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected code 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
var resp struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
Addresses []models.Address `json:"addresses"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
|
||||
t.Fatalf("failed to decode response: %v", err)
|
||||
}
|
||||
if len(resp.Data.Addresses) != 2 {
|
||||
t.Errorf("expected 2 addresses, got: %d", len(resp.Data.Addresses))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddressesIsolation(t *testing.T) {
|
||||
testDB, user1 := setupAddressTestDB(t)
|
||||
defer os.Remove(testDB)
|
||||
defer database.ResetForTesting()
|
||||
|
||||
db := database.GetDB()
|
||||
user2 := models.User{
|
||||
SSHKeyFingerprint: "SHA256:otheraddressuser",
|
||||
SSHPublicKey: "ssh-ed25519 AAAA otheraddresskey",
|
||||
}
|
||||
db.Create(&models.Address{
|
||||
UserID: user1.ID, Name: "User1 Addr", Street: "111 St",
|
||||
City: "Portland", State: "OR", Zip: "97201", Country: "US",
|
||||
})
|
||||
db.Create(&models.Address{
|
||||
UserID: user2.ID, Name: "User2 Addr", Street: "222 St",
|
||||
City: "Seattle", State: "WA", Zip: "98101", Country: "US",
|
||||
})
|
||||
|
||||
handler := NewAddressHandler("", "", "")
|
||||
|
||||
req := authRequest("GET", "/api/v1/addresses", nil, user1.ID)
|
||||
w := httptest.NewRecorder()
|
||||
handler.GetAddresses(w, req)
|
||||
|
||||
var resp struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
Addresses []interface{} `json:"addresses"`
|
||||
} `json:"data"`
|
||||
}
|
||||
json.NewDecoder(w.Body).Decode(&resp)
|
||||
|
||||
if len(resp.Data.Addresses) != 1 {
|
||||
t.Fatalf("expected to get 1 address for user1, found: %d", len(resp.Data.Addresses))
|
||||
}
|
||||
if resp.Data.Addresses[0].Name != "User1 Addr" {
|
||||
t.Errorf("expected 'User1 Addr', got %q", resp.Data.Addresses[0].Name)
|
||||
}
|
||||
}
|
||||
@@ -33,13 +33,16 @@ func (h *CardHandler) GetCards(w http.ResponseWriter, r *http.Request) {
|
||||
userID := middleware.UserIDFromContext(r.Context())
|
||||
|
||||
var cards []models.Card
|
||||
db.Where("user_id = ?", userID).Order("created_at DESC").Find(&cards)
|
||||
db.Where("user_id = ?", userID).Order("is_default DESC, created_at DESC").Find(&cards)
|
||||
|
||||
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
|
||||
"cards": cards,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
// func utils.RespondError(w http.ResponseWriter, statusCode int, code string, message string, details map[string]interface{})
|
||||
|
||||
// GetCard returns a single saved card by ID.
|
||||
func (h *CardHandler) GetCard(w http.ResponseWriter, r *http.Request) {
|
||||
db := database.GetDB()
|
||||
@@ -63,6 +66,33 @@ func (h *CardHandler) GetCard(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
func (h *CardHandler) SetDefaultCard(w http.ResponseWriter, r *http.Request) {
|
||||
db := database.GetDB()
|
||||
userID := middleware.UserIDFromContext(r.Context())
|
||||
|
||||
idStr := chi.URLParam(r, "id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
utils.RespondError(w, http.StatusBadRequest, "INVALID_ID", "invalid card id", nil)
|
||||
return
|
||||
}
|
||||
|
||||
var card models.Card
|
||||
if err := db.Where("id = ? AND user_id = ?", id, userID).First(&card).Error; err != nil {
|
||||
utils.RespondError(w, http.StatusNotFound, "CARD_NOT_FOUND", "card not found", nil)
|
||||
return
|
||||
}
|
||||
|
||||
db.Model(&models.Card{}).Where("user_id = ?", userID).Update("id_default", false)
|
||||
|
||||
card.IsDefault = true
|
||||
db.Save(&card)
|
||||
|
||||
utils.RespondSuccess(w, http.StatusOK, map[string]interface{}{
|
||||
"card": card,
|
||||
})
|
||||
}
|
||||
|
||||
// SaveCard creates a saved card from a Stripe token. The token is converted to
|
||||
// a PaymentMethod and attached to the user's Stripe customer.
|
||||
func (h *CardHandler) SaveCard(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -204,3 +234,4 @@ func (h *CardHandler) DeleteCard(w http.ResponseWriter, r *http.Request) {
|
||||
"message": "card deleted",
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ func SetupRoutes(version string, stripeSecretKey string, jwtManager *auth.JWTMan
|
||||
r.Get("/{id}", cardHandler.GetCard)
|
||||
r.Post("/", cardHandler.SaveCard)
|
||||
r.Delete("/{id}", cardHandler.DeleteCard)
|
||||
r.Put("/{id}/default", cardHandler.SetDefaultCard)
|
||||
})
|
||||
|
||||
// Orders
|
||||
|
||||
@@ -728,3 +728,23 @@ func (c *Client) DeleteAddress(id uint) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func(c *Client) SetDefaultAddress(id uint) error {
|
||||
url := fmt.Sprintf("%s/api/v1/addresses/%d/default", c.BaseURL, id)
|
||||
req, err := http.NewRequest("PUT", url, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
req.Header.Set("Authorization", "Bearer "+c.Token)
|
||||
|
||||
resp, err := c.HTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("failed to set default address: status %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -125,6 +125,8 @@ func (m Model) BuildAccountView(availableHeight int) string {
|
||||
aboutContent += userStyle.Render("@iamdavidhill") + "\n\n"
|
||||
aboutContent += contentStyle.Render("And me:") + "\n\n"
|
||||
aboutContent += userStyle.Render("@marcusDenslow") + "\n\n"
|
||||
aboutContent += contentStyle.Render(wordWrap("This project tries to copy the idea of terminalDotShop, but with one twist: write everything in GO. this is my humble attempt",
|
||||
detailContentWidth)) + "\n"
|
||||
aboutContent += accentStyle.Render("Terminal Products, Inc.")
|
||||
|
||||
detailView = titleStyle.Render("About") + "\n\n" + aboutContent
|
||||
|
||||
Submodule terminal-shop-source deleted from 697730e73c
Reference in New Issue
Block a user