# Copyright © 2022 Aravinth Manivannan # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import time from io import StringIO from django.contrib.auth import get_user_model from django.core import mail from django.core.management import call_command from django.urls import reverse from django.test import TestCase, Client, override_settings from django.utils.http import urlencode from django.contrib.auth import authenticate from django.core.management import call_command from django.conf import settings from payments import get_payment_model, RedirectNeeded, PaymentStatus from accounts.tests import register_util, login_util from dash.tests import create_configurations, create_instance_util, infra_custom_config from dash.models import Instance from .models import Payment from .utils import payment_fullfilled class BillingTest(TestCase): """ Tests billing system """ def setUp(self): self.username = "billing_user" register_util(t=self, username=self.username) create_configurations(t=self) @override_settings(HOSTEA=infra_custom_config(test_name="test_payments")) def test_payments(self): c = Client() login_util(self, c, "accounts.home") instance_name = "tpayments" create_instance_util( t=self, c=c, instance_name=instance_name, config=self.instance_config[0] ) instance = Instance.objects.get(name=instance_name) self.assertEqual(payment_fullfilled(instance=instance), True) payment = get_payment_model().objects.get(paid_by=self.user) invoice_uri = reverse("billing.invoice.details", args=(payment.public_ref,)) # check if paid invoice is listed in paid invoice list view resp = c.get(reverse("billing.invoice.paid")) self.assertEqual(str.encode(invoice_uri) in resp.content, True) # check if the paid invoice is displayed in the pending invoice view, should not be displayed resp = c.get(reverse("billing.invoice.pending")) self.assertEqual(str.encode(invoice_uri) in resp.content, False) # check if the invoice details view is rendering paid invoice version resp = c.get(invoice_uri) self.assertEqual(str.encode(instance_name) in resp.content, True) self.assertEqual( str.encode(str(self.instance_config[0].rent)) in resp.content, True ) self.assertEqual(str.encode("Paid on") in resp.content, True) # try to generate an invoice for the second time on the same VM # shouldn't be possible since payment is already made for the duration payment_uri = reverse("billing.invoice.generate", args=(instance.name,)) resp = c.get(payment_uri) self.assertEqual(resp.status_code, 400) ## payment success page; no real functionality but user is redirected here # by stripe if payment is successful resp = c.get(reverse("billing.invoice.success", args=(payment.public_ref,))) self.assertEqual( resp.headers["Location"], reverse("infra.create", args=(payment.instance_name,)), ) # create_instance_util creates an instance and pays for it. An email is # sent when the invoice is generated and one after payment is made # # So we are first checking for the last email that was sent(receipt) # and then the Forgejo instance credentials notification followed by the # invoice generation email. receipt_mail = mail.outbox.pop() print(receipt_mail.body) self.assertEqual( all( [ receipt_mail.to[0] == self.email, "This is a receipt for your latest Gna! payment" in receipt_mail.body, ] ), True, ) instance_notificaiton = mail.outbox.pop() self.assertEqual( all( [ instance_notificaiton.to[0] == self.email, "Congratulations on your new Gna! instance!" in instance_notificaiton.body, ] ), True, ) ## payment failure page; no real functionality but user is redirected here # by stripe if payment is successful resp = c.get(reverse("billing.invoice.fail", args=(payment.public_ref,))) self.assertEqual(b"failed" in resp.content, True) class GenerateInvoiceCommand(TestCase): """ Test command: manage.py generate_invoice """ def setUp(self): self.username = "test_generate_invoice_cmd_user" register_util(t=self, username=self.username) create_configurations(t=self) @override_settings( HOSTEA=infra_custom_config( test_name="test_dont_send_invoices_to_not_created_vms" ) ) def test_dont_send_invoices_to_not_created_vms(self): c = Client() login_util(self, c, "accounts.home") instance_name = "tnoinvonocrevm" payload = {"name": instance_name, "configuration": self.instance_config[0].name} resp = c.post(reverse("dash.instances.new"), payload) self.assertEqual(resp.status_code, 302) self.assertEqual( resp.headers["location"], reverse("billing.invoice.generate", args=(instance_name,)), ) stdout = StringIO() stderr = StringIO() instance = Instance.objects.get(name=instance_name) self.assertEqual(payment_fullfilled(instance=instance), False) prev_len = len(mail.outbox) # username exists call_command( "generate_invoice", stdout=stdout, stderr=stderr, ) out = stdout.getvalue() print(out) self.assertEqual(instance_name not in out, True) @override_settings( HOSTEA=infra_custom_config(test_name="test_generate_invoice_cmd") ) def test_cmd(self): c = Client() login_util(self, c, "accounts.home") instance_name = "tgeninvmd" create_instance_util( t=self, c=c, instance_name=instance_name, config=self.instance_config[0] ) stdout = StringIO() stderr = StringIO() instance = Instance.objects.get(name=instance_name) self.assertEqual(payment_fullfilled(instance=instance), True) prev_len = len(mail.outbox) # username exists call_command( "generate_invoice", stdout=stdout, stderr=stderr, ) out = stdout.getvalue() print(out) self.assertEqual(instance_name in out, True) self.assertEqual(prev_len, len(mail.outbox)) # delete payment and re-generate with command get_payment_model().objects.get(instance_name=instance_name).delete() stdout = StringIO() stderr = StringIO() call_command( "generate_invoice", stdout=stdout, stderr=stderr, ) out = stdout.getvalue() print("out") print(out) self.assertEqual(instance_name in out, True) self.assertEqual(f"Payment not fulfilled for instance: {instance}" in out, True) self.assertEqual(prev_len + 1, len(mail.outbox))