Skip to content

Commit

Permalink
Merge pull request #197 from UnitapApp/feature/reversed-constraint
Browse files Browse the repository at this point in the history
Prizetap reversed constraints
  • Loading branch information
ShayanShiravani authored Nov 25, 2023
2 parents ca0570d + 1e907a4 commit dcf08f3
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 3 deletions.
18 changes: 18 additions & 0 deletions prizetap/migrations/0044_raffle_reversed_constraints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.0.4 on 2023-11-23 09:12

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('prizetap', '0043_constraint_explanation'),
]

operations = [
migrations.AddField(
model_name='raffle',
name='reversed_constraints',
field=models.TextField(blank=True, null=True),
),
]
1 change: 1 addition & 0 deletions prizetap/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class Meta:

constraints = models.ManyToManyField(Constraint, blank=True, related_name="raffles")
constraint_params = models.TextField(null=True, blank=True)
reversed_constraints = models.TextField(null=True, blank=True)

created_at = models.DateTimeField(auto_now_add=True, editable=True)
start_at = models.DateTimeField(default=timezone.now)
Expand Down
8 changes: 8 additions & 0 deletions prizetap/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ def validate(self, data):
constraints = data["constraints"]
constraint_params = json.loads(base64.b64decode(data["constraint_params"]))
data["constraint_params"] = base64.b64decode(data["constraint_params"]).decode("utf-8")
reversed_constraints = []
if "reversed_constraints" in data:
reversed_constraints = str(data["reversed_constraints"]).split(",")
if len(constraints) != 0:
for c in constraints:
constraint_class: ConstraintVerification = get_constraint(c.name)
Expand All @@ -121,6 +124,11 @@ def validate(self, data):
constraint_class.is_valid_param_keys(constraint_params[c.name])
except KeyError as e:
raise serializers.ValidationError({"constraint_params": [{f"{c.name}": str(e)}]})
valid_constraints = [c.name for c in constraints]
if len(reversed_constraints) > 0:
for c in reversed_constraints:
if c not in valid_constraints:
raise serializers.ValidationError({"reversed_constraints": [{f"{c}": "Invalid constraint name"}]})
if "winners_count" in data and data["winners_count"] > data["max_number_of_entries"]:
raise serializers.ValidationError({"winners_count": "Invalid value"})
valid_chains = list(CONTRACT_ADDRESSES.keys())
Expand Down
51 changes: 50 additions & 1 deletion prizetap/tests.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import base64
import json
from unittest.mock import PropertyMock, patch
from unittest.mock import MagicMock, PropertyMock, patch

from django.contrib.auth.models import User
from django.urls import reverse
from django.utils import timezone
from rest_framework.exceptions import PermissionDenied
from rest_framework.test import APITestCase

from authentication.helpers import BrightIDSoulboundAPIInterface
from authentication.models import NetworkTypes, UserProfile, Wallet
from faucet.models import Chain, WalletAccount

from .models import Constraint, NotHaveUnitapPass, Raffle, RaffleEntry
from .validators import RaffleEnrollmentValidator

# from .utils import PrizetapContractClient

Expand Down Expand Up @@ -235,6 +238,52 @@ def test_create_raffle_with_invalid_constraint_params(self):

self.assertEqual(raffle, None)

@patch(
"authentication.helpers.BrightIDSoulboundAPIInterface.get_verification_status",
lambda a, b, c: (True, None),
)
def test_reversed_constraints(self):
self.raffle.reversed_constraints = "core.BrightIDMeetVerification"
self.raffle.save()
validator = RaffleEnrollmentValidator(user_profile=self.user_profile, raffle=self.raffle)
self.assertRaises(PermissionDenied, validator.check_user_constraints)

def test_create_raffle_with_invalid_reversed_constraints(self):
self.client.force_authenticate(user=self.user_profile.user)
self.raffle_data["constraints"] = [self.meet_constraint.pk]
self.raffle_data["reversed_constraints"] = "core.BrightIDAuraVerification"
response = self.client.post(reverse("create-raffle"), self.raffle_data)
self.assertEqual(
str(response.data["reversed_constraints"][0]["core.BrightIDAuraVerification"]), "Invalid constraint name"
)
self.assertEqual(response.status_code, 400)
self.assertEqual(Raffle.objects.count(), 1)
raffle = None
try:
raffle = Raffle.objects.get(pk=2)
except Raffle.DoesNotExist:
pass

self.assertEqual(raffle, None)

@patch(
"authentication.helpers.BrightIDSoulboundAPIInterface.get_verification_status",
lambda a, b, c: (False, None),
)
def test_create_raffle_with_reversed_constraints(self):
self.client.force_authenticate(user=self.user_profile.user)
self.raffle_data["constraints"] = [self.meet_constraint.pk]
self.raffle_data["reversed_constraints"] = self.meet_constraint.name
response = self.client.post(reverse("create-raffle"), self.raffle_data)
self.assertEqual(response.status_code, 200)
self.assertEqual(Raffle.objects.count(), 2)
raffle = Raffle.objects.get(pk=response.data["data"]["id"])
self.assertEqual(raffle.name, self.raffle_data["name"])
validator = RaffleEnrollmentValidator(user_profile=self.user_profile, raffle=raffle)
validator.check_user_constraints()
BrightIDSoulboundAPIInterface.get_verification_status = MagicMock(return_value=(True, None))
self.assertRaises(PermissionDenied, validator.check_user_constraints)

def test_create_raffle_with_invalid_winners_count(self):
self.client.force_authenticate(user=self.user_profile.user)
self.raffle_data["max_number_of_entries"] = 10
Expand Down
9 changes: 7 additions & 2 deletions prizetap/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,20 @@ def check_user_constraints(self):
param_values = json.loads(self.raffle.constraint_params)
except Exception:
param_values = {}
reversed_constraints = self.raffle.reversed_constraints.split(",") if self.raffle.reversed_constraints else []
for c in self.raffle.constraints.all():
constraint: ConstraintVerification = get_constraint(c.name)(self.user_profile)
constraint.response = c.response
try:
constraint.param_values = param_values[c.name]
except KeyError:
pass
if not constraint.is_observed():
raise PermissionDenied(constraint.response)
if c.name in reversed_constraints:
if constraint.is_observed():
raise PermissionDenied(constraint.response)
else:
if not constraint.is_observed():
raise PermissionDenied(constraint.response)

def check_user_has_wallet(self):
if not self.user_profile.wallets.filter(wallet_type=self.raffle.chain.chain_type).exists():
Expand Down

0 comments on commit dcf08f3

Please sign in to comment.