~netlandish/links

1486b52727f04f174de532f91e47c364b89377a7 — Yader Velasquez 9 months ago 2bdcb56
Add more tests to analytics and billing
M analytics/routes_test.go => analytics/routes_test.go +53 -1
@@ 1,6 1,7 @@
package analytics_test

import (
	"fmt"
	"links/analytics"
	"links/cmd"
	"links/models"


@@ 11,13 12,64 @@ import (
	"time"

	"git.sr.ht/~emersion/gqlclient"
	"github.com/jarcoal/httpmock"
	"github.com/labstack/echo/v4"
	"github.com/stretchr/testify/require"
	oauth2 "netlandish.com/x/gobwebs-oauth2"
	"netlandish.com/x/gobwebs/crypto"
	"netlandish.com/x/gobwebs/server"
)

func TestHandlers(t *testing.T) {
func TestHandler(t *testing.T) {
	c := require.New(t)
	srv, e := cmd.NewWebTestServer(t)
	cmd.RunMigrations(t, srv.DB)
	analyticsService := analytics.NewService(e.Group("/analytics"))
	user := cmd.NewTestUser(1, false, false, true, true)
	defer srv.Shutdown()
	go srv.Run()
	dbCtx := cmd.NewDBContext(srv.DB, "America/Managua")

	httpmock.Activate()
	defer httpmock.DeactivateAndReset()
	jsonResponse, err := httpmock.NewJsonResponder(http.StatusOK, httpmock.File("samples/analytics.json"))
	c.NoError(err)
	httpmock.RegisterResponder("POST", "http://127.0.0.1:8080/query", jsonResponse)

	t.Run("checkout success", func(t *testing.T) {
		short := &models.LinkShort{
			Title:     "Testing short",
			URL:       "http://testing.com",
			ShortCode: "abc",
			OrgID:     1,
			UserID:    int(user.ID),
			DomainID:  1,
		}
		err := short.Store(dbCtx)
		c.NoError(err)
		req := httptest.NewRequest(http.MethodGet, "/", nil)

		// Add fake analytics entries
		err = analytics.AddAnalytics(dbCtx, req, short.ID, analytics.LinkShortAnalyticsFilter, "")
		c.NoError(err)
		request := httptest.NewRequest(http.MethodGet, "/shorts/1", nil)
		request.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)
		recorder := httptest.NewRecorder()
		ctx := &server.Context{
			Server:  srv,
			Context: e.NewContext(request, recorder),
			User:    user,
		}
		ctx.SetPath("/:slug/analytics/:type/:id")
		ctx.SetParamNames("slug", "type", "id")
		ctx.SetParamValues("personal-org", "shorts", fmt.Sprint(short.ID))
		err = cmd.MakeRequest(srv, analyticsService.Detail, ctx)
		c.NoError(err)
		c.Equal(http.StatusOK, recorder.Code)
	})
}

func TestAPI(t *testing.T) {
	c := require.New(t)
	srv, e, entropy := cmd.NewAPITestServer(t)
	cmd.RunMigrations(t, srv.DB)

A analytics/samples/analytics.json => analytics/samples/analytics.json +68 -0
@@ 0,0 1,68 @@
{
    "data": {
        "analytics": {
            "title": "New Listst",
            "clicksOverTime": [
                {
                    "key": "2024-01-21",
                    "val": 10
                },
                {
                    "key": "2024-01-28",
                    "val": 10
                },
                {
                    "key": "2024-02-04",
                    "val": 10
                },
                {
                    "key": "2024-02-11",
                    "val": 10
                },
                {
                    "key": "2024-01-14",
                    "val": 10
                }
            ],
            "listingLink": [
                {
                    "id": 12,
                    "title": "Title",
                    "clicks": 12
                }
            ],
            "qrList": [
                {
                    "id": 12,
                    "title": "Title",
                    "clicks": 12
                }
            ],
            "clicksCountry": [
                {
                    "key": "Country",
                    "val": 1000
                }
            ],
            "clicksCity": [
                {
                    "key": "City",
                    "val": 1000
                }

            ],
            "clicksDevice": [
                {
                    "key": "Device",
                    "val": 1000
                }
            ],
            "clicksReferrer": [
                {
                    "key": "Referrer",
                    "val": 1000
                }
            ]
        }
    }
}

M billing/routes_test.go => billing/routes_test.go +40 -0
@@ 30,6 30,46 @@ func TestHandlers(t *testing.T) {
	go srv.Run()
	dbCtx := cmd.NewDBContext(srv.DB, "America/Managua")

	t.Run("checkout success", func(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()
		checkoutSession, err := httpmock.NewJsonResponder(http.StatusOK, httpmock.File("samples/get_checkout_session.json"))
		c.NoError(err)
		httpmock.RegisterResponder("GET", "https://api.stripe.com/v1/checkout/sessions/sid", checkoutSession)

		invoiceList, err := httpmock.NewJsonResponder(http.StatusOK, httpmock.File("samples/invoice_list.json"))
		c.NoError(err)
		httpmock.RegisterResponder("GET", "https://api.stripe.com/v1/invoices", invoiceList)

		request := httptest.NewRequest(http.MethodGet, "/sucess?session_id=sid", nil)
		request.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)
		recorder := httptest.NewRecorder()
		ctx := &server.Context{
			Server:  srv,
			Context: e.NewContext(request, recorder),
			User:    user,
		}
		ctx.SetPath("/success")
		err = cmd.MakeRequest(srv, billingService.CheckoutSuccess, ctx)
		c.NoError(err)
		c.Equal(http.StatusOK, recorder.Code)
	})

	t.Run("checkou cancel", func(t *testing.T) {
		request := httptest.NewRequest(http.MethodGet, "/cancel", nil)
		request.Header.Set(echo.HeaderContentType, echo.MIMEApplicationForm)
		recorder := httptest.NewRecorder()
		ctx := &server.Context{
			Server:  srv,
			Context: e.NewContext(request, recorder),
			User:    user,
		}
		ctx.SetPath("/cancel")
		err := cmd.MakeRequest(srv, billingService.CheckoutCancel, ctx)
		c.NoError(err)
		c.Equal(http.StatusOK, recorder.Code)
	})

	t.Run("create subscription", func(t *testing.T) {
		httpmock.Activate()
		defer httpmock.DeactivateAndReset()

A billing/samples/get_checkout_session.json => billing/samples/get_checkout_session.json +74 -0
@@ 0,0 1,74 @@
{
  "id": "cs_test_a11YYufWQzNY63zpQ6QSNRQhkUpVph4WRmzW0zWJO2znZKdVujZ0N0S22u",
  "object": "checkout.session",
  "after_expiration": null,
  "allow_promotion_codes": null,
  "amount_subtotal": 2198,
  "amount_total": 2198,
  "automatic_tax": {
    "enabled": false,
    "liability": null,
    "status": null
  },
  "billing_address_collection": null,
  "cancel_url": null,
  "client_reference_id": null,
  "consent": null,
  "consent_collection": null,
  "created": 1679600215,
  "currency": "usd",
  "custom_fields": [],
  "custom_text": {
    "shipping_address": null,
    "submit": null
  },
  "customer": null,
  "customer_creation": "if_required",
  "customer_details": null,
  "customer_email": null,
  "expires_at": 1679686615,
  "invoice": null,
  "invoice_creation": {
    "enabled": false,
    "invoice_data": {
      "account_tax_ids": null,
      "custom_fields": null,
      "description": null,
      "footer": null,
      "issuer": null,
      "metadata": {},
      "rendering_options": null
    }
  },
  "livemode": false,
  "locale": null,
  "metadata": {},
  "mode": "payment",
  "payment_intent": null,
  "payment_link": null,
  "payment_method_collection": "always",
  "payment_method_options": {},
  "payment_method_types": [
    "card"
  ],
  "payment_status": "unpaid",
  "phone_number_collection": {
    "enabled": false
  },
  "recovered_from": null,
  "setup_intent": null,
  "shipping_address_collection": null,
  "shipping_cost": null,
  "shipping_details": null,
  "shipping_options": [],
  "status": "open",
  "submit_type": null,
  "subscription": "12332",
  "success_url": "https://example.com/success",
  "total_details": {
    "amount_discount": 0,
    "amount_shipping": 0,
    "amount_tax": 0
  },
  "url": "https://checkout.stripe.com/c/pay/cs_test_a11YYufWQzNY63zpQ6QSNRQhkUpVph4WRmzW0zWJO2znZKdVujZ0N0S22u#fidkdWxOYHwnPyd1blpxYHZxWjA0SDdPUW5JbmFMck1wMmx9N2BLZjFEfGRUNWhqTmJ%2FM2F8bUA2SDRySkFdUV81T1BSV0YxcWJcTUJcYW5rSzN3dzBLPUE0TzRKTTxzNFBjPWZEX1NKSkxpNTVjRjN8VHE0YicpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl"
}

A billing/samples/invoice_list.json => billing/samples/invoice_list.json +108 -0
@@ 0,0 1,108 @@
{
  "object": "list",
  "url": "/v1/invoices",
  "has_more": false,
  "data": [
    {
      "id": "in_1MtHbELkdIwHu7ixl4OzzPMv",
      "object": "invoice",
      "account_country": "US",
      "account_name": "Stripe Docs",
      "account_tax_ids": null,
      "amount_due": 0,
      "amount_paid": 0,
      "amount_remaining": 0,
      "amount_shipping": 0,
      "application": null,
      "application_fee_amount": null,
      "attempt_count": 0,
      "attempted": false,
      "auto_advance": false,
      "automatic_tax": {
        "enabled": false,
        "liability": null,
        "status": null
      },
      "billing_reason": "manual",
      "charge": null,
      "collection_method": "charge_automatically",
      "created": 1680644467,
      "currency": "usd",
      "custom_fields": null,
      "customer": "cus_NeZwdNtLEOXuvB",
      "customer_address": null,
      "customer_email": "jennyrosen@example.com",
      "customer_name": "Jenny Rosen",
      "customer_phone": null,
      "customer_shipping": null,
      "customer_tax_exempt": "none",
      "customer_tax_ids": [],
      "default_payment_method": null,
      "default_source": null,
      "default_tax_rates": [],
      "description": null,
      "discount": null,
      "discounts": [],
      "due_date": null,
      "ending_balance": null,
      "footer": null,
      "from_invoice": null,
      "hosted_invoice_url": null,
      "invoice_pdf": null,
      "issuer": {
        "type": "self"
      },
      "last_finalization_error": null,
      "latest_revision": null,
      "lines": {
        "object": "list",
        "data": [],
        "has_more": false,
        "total_count": 0,
        "url": "/v1/invoices/in_1MtHbELkdIwHu7ixl4OzzPMv/lines"
      },
      "livemode": false,
      "metadata": {},
      "next_payment_attempt": null,
      "number": null,
      "on_behalf_of": null,
      "paid": false,
      "paid_out_of_band": false,
      "payment_intent": null,
      "payment_settings": {
        "default_mandate": null,
        "payment_method_options": null,
        "payment_method_types": null
      },
      "period_end": 1680644467,
      "period_start": 1680644467,
      "post_payment_credit_notes_amount": 0,
      "pre_payment_credit_notes_amount": 0,
      "quote": null,
      "receipt_number": null,
      "rendering_options": null,
      "shipping_cost": null,
      "shipping_details": null,
      "starting_balance": 0,
      "statement_descriptor": null,
      "status": "draft",
      "status_transitions": {
        "finalized_at": null,
        "marked_uncollectible_at": null,
        "paid_at": null,
        "voided_at": null
      },
      "subscription": null,
      "subtotal": 0,
      "subtotal_excluding_tax": 0,
      "tax": null,
      "test_clock": null,
      "total": 0,
      "total_discount_amounts": [],
      "total_excluding_tax": 0,
      "total_tax_amounts": [],
      "transfer_data": null,
      "webhooks_delivered_at": 1680644467
    }
  ]
}