From 8cb6abef935df8000bf82e5f61994362959ba2da Mon Sep 17 00:00:00 2001 From: Keshav Arora Date: Tue, 23 Dec 2025 13:36:07 +0530 Subject: [PATCH 1/8] Update .gitignore to include additional files --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2b65a13..30fa2fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ *.pyc dist openpaygo.egg-info -.DS_store \ No newline at end of file +.DS_store +__pycache__ +venv From 1890400cc4e4acb72c1d59f4f6fbdcdafe75f2bb Mon Sep 17 00:00:00 2001 From: Keshav Arora Date: Tue, 23 Dec 2025 13:37:06 +0530 Subject: [PATCH 2/8] added simulator --- simulators/__init__.py | 2 + .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 336 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 322 bytes .../device_simulator.cpython-311.pyc | Bin 0 -> 7782 bytes .../device_simulator.cpython-314.pyc | Bin 0 -> 7694 bytes .../server_simulator.cpython-311.pyc | Bin 0 -> 5719 bytes .../server_simulator.cpython-314.pyc | Bin 0 -> 5705 bytes simulators/device_simulator.py | 131 ++++++++++++++++++ simulators/server_simulator.py | 97 +++++++++++++ 9 files changed, 230 insertions(+) create mode 100644 simulators/__init__.py create mode 100644 simulators/__pycache__/__init__.cpython-311.pyc create mode 100644 simulators/__pycache__/__init__.cpython-314.pyc create mode 100644 simulators/__pycache__/device_simulator.cpython-311.pyc create mode 100644 simulators/__pycache__/device_simulator.cpython-314.pyc create mode 100644 simulators/__pycache__/server_simulator.cpython-311.pyc create mode 100644 simulators/__pycache__/server_simulator.cpython-314.pyc create mode 100644 simulators/device_simulator.py create mode 100644 simulators/server_simulator.py diff --git a/simulators/__init__.py b/simulators/__init__.py new file mode 100644 index 0000000..618cc96 --- /dev/null +++ b/simulators/__init__.py @@ -0,0 +1,2 @@ +from .device_simulator import DeviceSimulator +from .server_simulator import SingleDeviceServerSimulator \ No newline at end of file diff --git a/simulators/__pycache__/__init__.cpython-311.pyc b/simulators/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8d006c0d3d4fbd40fc3555759e675906d881bea GIT binary patch literal 336 zcmZ3^%ge<81YgoTGt+_eV-N=hn4pZ$azMs(h7^Vr#vF!R#wbQc5SuB7DVI5l8OUZ1 zX3%7L$p}=W$#{$3CABOwIW;&lw=^fQB)kdHZJb-ODlNdG?mNt|C#QUAb7F4?n%mk}u3qBx496VwDXLQT*kG>vsj!ZKkU zv68YiVVkgz*e4t#4w|x1$0*MBCB@nKvinwwdIbMQ8eurc5EUwWfz^;jXSkTeOYsR_ zFGtAn2`Lt$_3D91J~`Mkd~6_^8t0QkS7HLs!SHp3?O}d2#qok(PDVwiC;8FrIwZ4X zD=$w%;TFYHBQ!^iSU3;RMvquI`vyZc!OJL$OdYQpN%` zoJ3N_0ks^xRK^B1^UN}KsM&HOImD8?8!BUknj_aId2=O-8e?+h`%rk4t57h$JXhh6 z2EqZ@@}9Gux!IQymn&D}C^#2uBr4!8LT=qHN-;r-Coi*jN4j;KpVnP?$1E4W8pm6! z7I;w-;-eDJLEGiH#EQJ6ufG!3*A5UJ5CE^?_AqN2wFkY`UDm+Y`W-lgEqvNnPbi@<7b4r9V z8779xbeqU0E(y3h!HtV}^0tF3<4FrX1Rek)UVFeuKc8+pEAoQaHqMJzVprS3scXqZ zD#nRzCiAvVPD@u($+i@5Ic_V}Cg%7^Y~u)Hv1ZGz$!VQo*?1BTh)vfN@!hUm4_>!8 z2H*yzZQ3zwQ<*x=zkb&C6{FQ}$7-$Cco6d~T2mM1!DsaWqkUZvhE=R=ZoZxQ`ugqb z*jTRx8!->8g!U}eKWtcPz*>{GaTDe>GWlM@YSgI7!%)FvARM28)?1X6*T>QKq4vmZ zp$mI0lF9EB@YR?p3x%`Nus~r;5guz19usTftY5@qE5c*S-v{Ec7vfnkB^Negra}q* zz!gf)l}Mr^lCEuiTS`~)!B5D|a|59;4QpCNuwXL?4Z_*EDh za_II+Ar9hX1^E@KVH-jff=U2-d0b>;WaL9ysDTy{uRI{sVW*4CV?marDKTAN%**+` zV2&v99)KIv3gebNA%)qYGCO2u2dQqkw{JmGn-3|$E;ZPtc)AqkBbE6`W+yW(kAm;)+vKxPg=_w3jm|9tc> z7yf)f+0?3TYE^1?skOTl&n|^&Q<*lIX(Kq+r~!iD6Y7D|0#QK5Bcjj=1yKGJ%BGR> zSt5_op>s`5j}wLil}blKR^yBjo8U*V1wh`h3IV96Yl~0Qyod%Ij^_!g!RuGO!FyL0 z6L*t~$z|_;nb~j9ZVC#F!?g+R-$9{Jj59^-#?j+MRE2H%yDAQ$6lp+eCNN-@ z`_S{q>>Z+h5mP>`@5B_cL|&k^3)ysQf=}vJQ6so6N+_jSeXirIe6zu`y!!Dau!$F)yRDE>j! zKPbBfpBD}2S@!ol2|fKx@eiy1Vc9iY%CY9HKYLl%-CkEGZt`=tQi}$fWv|6nzQCgGZqPLay9|~ z0A*{2We8kPkQ;j>?gP{Oq6>z-Ks}^0@#H16qI{@~sH!5V(rsL9TGY!#a4M6WsM`f_ z8Mu($xL}LK340NX{Yq*|fMFByskZBRPU?K*~B`>G97GH7c zm=IveS#PITb=I#^j`9Yra-CYa?Hgvv_0Y5AQ7RA29?^o^7CRmsS^DI$P1)J?Sokje zbnoA8?Iv(u3+o^19eIO`xdldhk+3e9t`mj(oF)dl|1ExnNw$FhTfw62e_8CXUdgEHgCLv#B)0oLp zS%p?gxvh}VfO7i~RkA-v@u&>-1wDpdB1v3X8de&$ux88qoUODuHy=)rfnA!j({LtB z<%MgaxPWhNPT?wmPKb9tcxRObH|OMB8EauYM^hM`tlfoSwnSgXDh15tsWJ2nNXk-B zMq-+^8^q|xyf@AbHFJMpIMBH$NvIqO4F}vu83?{7qWhx*C;B36UnF{Fm_2fGp!Y;y z_-M!>>;NozK7BNK-z088t1KXM>bp=M5TD72Um$Lo z;tjgkSQ0D^1h#qRX>A9F+eJbVBwavJ(Oo?!&-C?#huO13ePLl8wxbRfP*Lesy#r?> z(Y`b6&~T)e1#-{!=}qsjc7_Qf1cBB>aWM|D1(7-Pdi4sUg0$v3jF`(~Sle7|l^-e* zb0};FT~oEur#_#nCJqg)$gXM^Ugy)&$C+PCc8Cwup4{svgL zvU#PtcD_ZaZc?k85cS7k-r~q3NvtiY>L-Vhj+pD>2=8r798)P?-LN*=5 z6~-0cR@Jvv_H8Ajo*7+-v1?-Cv%kTSbgRB@+1Cw|=A*Yqmc7lgxB0=LA37syXKcA8 zCbz`2P21;eH!HNwt+-sh*1R3_Mj-h|SI4re(90O^Jyc%qBP5&@zt0>stjxt#%`U?8&e{12Lv`0s_@ z`5QImnnpyMn)UD2w3=`XsqF_)tO<$wl?c%BCgf&kV|WU$2jyNKLPWj1tv+{kg-Unj|bi(;vy;M zYmgUHbA0|>0o2KxUu3}ZEDB5UmD^zcOd)mV+S&EW$*Zcl9?mM7VS2RVM3o7ZlIb5| znDZu~D;QDghr${V@I;U5o_zb>7uLOJBPSvQpGMf;fm5e?BH>WE;RjTo>N(H$^hW#7 z^+fvzB8DemL`B4jV21jlY_$JWpT0F`h>IRr4TppXu3m*tMQpf#sOQMZzAUajxB~%w z9C*GUegl!KZfREV7i z6K{O)5Qn{4aR8=;T^b3aV0cflNG6K`)0^ci6ShbuljV!VeL(Rjho~Uv%2=0*Y-iSj znCwj$IWIVSpB2W*T0+hHj0LCPTnqMVmh&z6`%ww6J|UV!>)^9uAVC~s;WUE;VX`C28N{fzKwC;E-e|DCw>V4n6@^! z5LO%8mD=`MkLFx=r&g_PS$4L_&X$#6%VO8v?#1qJ`=4~n`_Cx*&!_-{XOQOtGWnjB zJmz`#uQazUX6{~JypC+%s0EwVV8{2NrwwxFd8PBb3NUy+4+Y`92%G>K>Z4JTh3II= zg+DnYWAJMYt5>k>M2eeAVA;j8znzLDj2;2K2VpCM0D@Ko2N9r4PwXWTZ*o)*DYBN~ z@?+#DAi!JBO7IOh5d2%Ux$NLO7lL|+y#<~`a==I1+d+79z~QiufRL6zUm0RDK~!)@QTN|~Uc%svdm-VBYM;eH~ZUBj?~35epDg{C5&J*zA}>&{F&)8?xb6|0BvX&#XkT5eMQq6wOxMG jr%@ep;ipl(@|(U@#|%y5e&3$WUHZxBef5q(gy8=I7g@Sr literal 0 HcmV?d00001 diff --git a/simulators/__pycache__/device_simulator.cpython-314.pyc b/simulators/__pycache__/device_simulator.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c29437b5be48864a1671cc0fbf188ee876ec872 GIT binary patch literal 7694 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 literal 0 HcmV?d00001 diff --git a/simulators/__pycache__/server_simulator.cpython-311.pyc b/simulators/__pycache__/server_simulator.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16056aaecaeeb1505c0d1475f99435f3f424b030 GIT binary patch literal 5719 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 Qm-f75HQ&9ap6$VZ0PL*(#Q*>R literal 0 HcmV?d00001 diff --git a/simulators/__pycache__/server_simulator.cpython-314.pyc b/simulators/__pycache__/server_simulator.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5437629b076f2d7f676191a714051b17af2f7d9 GIT binary patch literal 5705 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 datetime.now() + + def enter_token(self, token, show_result=True): + if len(token) == 9: + token_int = int(token) + return self._update_device_status_from_token(token_int, show_result) + else: + token_int = int(token) + return self._update_device_status_from_extended_token(token_int, show_result) + + def get_days_remaining(self): + if self.payg_enabled: + td = self.expiration_date - datetime.now() + days, hours, minutes = td.days, td.seconds//3600, (td.seconds//60)%60 + days = days + (hours + minutes/60)/24 + return round(days) + else: + return 'infinite' + + def _update_device_status_from_token(self, token, show_result=True): + if self.token_entry_blocked_until > datetime.now() and self.waiting_period_enabled: + if show_result: + print('TOKEN_ENTRY_BLOCKED') + return False + + token_value, token_type, token_count, updated_counts = OpenPAYGOTokenDecoder.get_activation_value_count_and_type_from_token( + token=token, + starting_code=self.starting_code, + key=self.key, + last_count=self.count, + restricted_digit_set=self.restricted_digit_set, + used_counts=self.used_counts + ) + if token_value is None: + if token_type == TokenType.ALREADY_USED: + if show_result: + print('OLD_TOKEN') + return -2 + if show_result: + print('TOKEN_INVALID') + self.invalid_token_count += 1 + self.token_entry_blocked_until = datetime.now() + timedelta(minutes=2**self.invalid_token_count) + return -1 + elif token_value == -2: + if show_result: + print('OLD_TOKEN') + return -2 + else: + if show_result: + print('TOKEN_VALID', ' | Value:', token_value) + if token_count > self.count or token_value == OpenPAYGOTokenShared.COUNTER_SYNC_VALUE: + self.count = token_count + self.used_counts = updated_counts + self.invalid_token_count = 0 + self._update_device_status_from_token_value(token_value, token_type) + return 1 + + def _update_device_status_from_extended_token(self, token, show_result=True): + if self.token_entry_blocked_until > datetime.now() and self.waiting_period_enabled: + if show_result: + print('TOKEN_ENTRY_BLOCKED') + + token_value, token_count = OpenPAYGOTokenDecoder.get_activation_value_count_from_extended_token( + token=token, + starting_code=self.starting_code, + key=self.key, + last_count=self.count, + restricted_digit_set=self.restricted_digit_set + ) + if token_value is None: + if show_result: + print('TOKEN_INVALID') + self.invalid_token_count += 1 + self.token_entry_blocked_until = datetime.now() + timedelta(minutes=2**self.invalid_token_count) + else: + if show_result: + print('Special token entered, value: '+str(token_value)) + + def _update_device_status_from_token_value(self, token_value, token_type): + if token_value <= OpenPAYGOTokenShared.MAX_ACTIVATION_VALUE: + if not self.payg_enabled and token_type == TokenType.SET_TIME: + self.payg_enabled = True + if self.payg_enabled: + self._update_expiration_date_from_value(token_value, token_type) + elif token_value == OpenPAYGOTokenShared.PAYG_DISABLE_VALUE: + self.payg_enabled = False + elif token_value != OpenPAYGOTokenShared.COUNTER_SYNC_VALUE: + # We do nothing if its the sync counter value, the counter has been synced already + print('COUNTER_SYNCED') + else: + # If it's another value we also do nothing, as they are not defined + print('UNKNOWN_COMMAND') + + def _update_expiration_date_from_value(self, toke_value, token_type): + number_of_days = toke_value/self.time_divider + if token_type == TokenType.SET_TIME: + self.expiration_date = datetime.now() + timedelta(days=number_of_days) + else: + if self.expiration_date < datetime.now(): + self.expiration_date = datetime.now() + self.expiration_date = self.expiration_date + timedelta(days=number_of_days) diff --git a/simulators/server_simulator.py b/simulators/server_simulator.py new file mode 100644 index 0000000..c1a7888 --- /dev/null +++ b/simulators/server_simulator.py @@ -0,0 +1,97 @@ +from datetime import datetime +from openpaygo.token_encode import OpenPAYGOTokenEncoder +from openpaygo.token_shared import OpenPAYGOTokenShared, TokenType + + +class SingleDeviceServerSimulator(object): + + def __init__(self, starting_code, key, starting_count=1, restricted_digit_set=False, time_divider=1): + self.starting_code = starting_code + self.key = key + self.count = starting_count + self.expiration_date = datetime.now() + self.furthest_expiration_date = datetime.now() + self.payg_enabled = True + self.time_divider = time_divider + self.restricted_digit_set = restricted_digit_set + + def print_status(self): + print('Expiration Date: '+ str(self.expiration_date)) + print('Current count: '+str(self.count)) + print('PAYG Enabled: '+str(self.payg_enabled)) + + def generate_payg_disable_token(self): + self.count, token = OpenPAYGOTokenEncoder.generate_token( + starting_code=self.starting_code, + secret_key=self.key, + value=0, + count=self.count, + token_type=TokenType.DISABLE_PAYG, + restricted_digit_set=self.restricted_digit_set + ) + return SingleDeviceServerSimulator._format_token(token) + + def generate_counter_sync_token(self): + self.count, token = OpenPAYGOTokenEncoder.generate_token( + starting_code=self.starting_code, + secret_key=self.key, + value=0, + count=self.count, + token_type=TokenType.COUNTER_SYNC, + restricted_digit_set=self.restricted_digit_set + ) + return SingleDeviceServerSimulator._format_token(token) + + def generate_token_from_date(self, new_expiration_date, force=False): + furthest_expiration_date = self.furthest_expiration_date + if new_expiration_date > self.furthest_expiration_date: + self.furthest_expiration_date = new_expiration_date + + if new_expiration_date > furthest_expiration_date: + # If the date is strictly above the furthest date activated, use ADD + value = self._get_value_to_activate(new_expiration_date, self.expiration_date, force) + self.expiration_date = new_expiration_date + return self._generate_token_from_value(value, mode=TokenType.ADD_TIME) + else: + # If the date is below or equal to the furthest date activated, use SET + value = self._get_value_to_activate(new_expiration_date, datetime.now(), force) + self.expiration_date = new_expiration_date + return self._generate_token_from_value(value, mode=TokenType.SET_TIME) + + def _generate_token_from_value(self, value, mode): + self.count, token = OpenPAYGOTokenEncoder.generate_token( + starting_code=self.starting_code, + secret_key=self.key, + value=value, + count=self.count, + token_type=mode, + restricted_digit_set=self.restricted_digit_set + ) + return SingleDeviceServerSimulator._format_token(token) + + def _generate_extended_value_token(self, value): + pass + + def _get_value_to_activate(self, new_time, reference_time, force_maximum=False): + if new_time <= reference_time: + return 0 + else: + days = self._timedelta_to_days(new_time - reference_time) + value = int(round(days*self.time_divider, 0)) + if value > OpenPAYGOTokenShared.MAX_ACTIVATION_VALUE: + if not force_maximum: + raise Exception('TOO_MANY_DAYS_TO_ACTIVATE') + else: + return OpenPAYGOTokenShared.MAX_ACTIVATION_VALUE # Will need to be activated again after those days + return value + + @staticmethod + def _timedelta_to_days(this_timedelta): + return this_timedelta.days + (this_timedelta.seconds / 3600 / 24) + + @staticmethod + def _format_token(token): + token = str(token) + if len(token) < 9: + token = '0' * (9 - len(token)) + token + return token From df6e66d40b7190b42eb6d60f1c58cbc65ce5a612 Mon Sep 17 00:00:00 2001 From: Keshav Arora Date: Tue, 23 Dec 2025 13:37:33 +0530 Subject: [PATCH 3/8] Delete simulators directory --- simulators/__init__.py | 2 - .../__pycache__/__init__.cpython-311.pyc | Bin 336 -> 0 bytes .../__pycache__/__init__.cpython-314.pyc | Bin 322 -> 0 bytes .../device_simulator.cpython-311.pyc | Bin 7782 -> 0 bytes .../device_simulator.cpython-314.pyc | Bin 7694 -> 0 bytes .../server_simulator.cpython-311.pyc | Bin 5719 -> 0 bytes .../server_simulator.cpython-314.pyc | Bin 5705 -> 0 bytes simulators/device_simulator.py | 131 ------------------ simulators/server_simulator.py | 97 ------------- 9 files changed, 230 deletions(-) delete mode 100644 simulators/__init__.py delete mode 100644 simulators/__pycache__/__init__.cpython-311.pyc delete mode 100644 simulators/__pycache__/__init__.cpython-314.pyc delete mode 100644 simulators/__pycache__/device_simulator.cpython-311.pyc delete mode 100644 simulators/__pycache__/device_simulator.cpython-314.pyc delete mode 100644 simulators/__pycache__/server_simulator.cpython-311.pyc delete mode 100644 simulators/__pycache__/server_simulator.cpython-314.pyc delete mode 100644 simulators/device_simulator.py delete mode 100644 simulators/server_simulator.py diff --git a/simulators/__init__.py b/simulators/__init__.py deleted file mode 100644 index 618cc96..0000000 --- a/simulators/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .device_simulator import DeviceSimulator -from .server_simulator import SingleDeviceServerSimulator \ No newline at end of file diff --git a/simulators/__pycache__/__init__.cpython-311.pyc b/simulators/__pycache__/__init__.cpython-311.pyc deleted file mode 100644 index f8d006c0d3d4fbd40fc3555759e675906d881bea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 336 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 diff --git a/simulators/__pycache__/device_simulator.cpython-314.pyc b/simulators/__pycache__/device_simulator.cpython-314.pyc deleted file mode 100644 index 7c29437b5be48864a1671cc0fbf188ee876ec872..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7694 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 diff --git a/simulators/__pycache__/server_simulator.cpython-311.pyc b/simulators/__pycache__/server_simulator.cpython-311.pyc deleted file mode 100644 index 16056aaecaeeb1505c0d1475f99435f3f424b030..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5719 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 Qm-f75HQ&9ap6$VZ0PL*(#Q*>R diff --git a/simulators/__pycache__/server_simulator.cpython-314.pyc b/simulators/__pycache__/server_simulator.cpython-314.pyc deleted file mode 100644 index c5437629b076f2d7f676191a714051b17af2f7d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5705 zcmd^DO;8)j74DHVVn9N|NLUy!2n&OSv9TR&V@&v2kYgMh$N<8uF=JU8K(QDpGXpj* zSBl$G6(4phTeYt2rs5=9m34B+9(~9mm26e6D8NcPg%elB7vCI1ZRM28dp*)<_%Z8E z<(5A5bibbW`gQl~@4eT@U8N-i%GUHi{5>}z|G-YZu(`%&4>Xp@6yccxa+#>4j!ap2 z%K&rPqFQL%I$*tQQ*Cvmg47e<<|90NlWnu={HmR|2S~8!C3b_1#>FD4sK)0+XqOHR z_fC9oC?rLbF;NDBQzwkfMr1J-)dlFaCD++}0F4OfbCakHPgKj4U9}!0Ny3-%jE^K3 z)y7+VL}hs^)OOTHE#h4~OUF3g4*g=j2Ew%jq%AjvS_=IVQ{TlqRCk!D zWxNws;?}B1;?hh)>=zf}QE^0+7esj^J~y9;s7X0$&q=`#Dh=DfZ9av@5*aU(kV?zA zUt)~*DJj1^GHLJ+bNTVYx86`POr-n)eH$J_I!{1CQC{-+uw=|O-Xj&~={y4qwu}x2 zOSDo&jmRo2T)@N9tarpkjg2PfB~`|g)~r(UuI3i+rQ&i#jVC1m&snRSo|n~GQBj4R zBhFN0aYhiO$nAs})0}7#LM*-z2Sd=jGH}T8s4B*QHWOC`MO1@!&8CQnX&JSX(QY-D zNheg~0FIPFrQkXnY0+j{#}rXkTJMO;Y-FLeKY3S5BqK4UbtolD)c#JS7S-9LWFm|v z?}$=s5`I7}lP6T#SMrCfw1Px9b-|#VOf71hAjBomRnQvtF_4q_zi`8qcc5A#*~86i zTvgU{@FAD22s|v=sB8MggP%S4_{Yn(HLhmk*s0$;f9w363wuYiHGcda%GMmix}p6` zu6`4wkf>o#(82Xn%X(`clXW9vM8y3YN$YP;eq=@y47{~2ZOA} zrsTM!YF4l~xfG_99asSx0$R!C@KJC>8f7oknv?!4fZeI{igs+jb2k4;#asaaOJsv9 zU2)Za`klvWrs-n3>EddjbKTXs#(kF#)O~7w-2a;^zr6Bm_qwZfjXOoDwVx=D+cJT+ zbf9gu?(Dkj?3dg**$3jbojL9sJ@dcOi6)`e%dmkic?tob-uv`{y2nXQ8fP+09Y#S8 zwt&aEosy%vc2jwBK8IU(K`X!@W407CiC34FVNbqQ_=cIhJq5@UCdis_fL(|r=0*MB z5CeiGG8(q#n-L`u;7G&+lVTA$CeWK)V3koBtsJdF=pPvAedlUO2=`tc3l)UPh>}{V zFrAd=BC0OniG^B*#!_(#?r)=Cw~%UMP6}|bxPl0$vuWq|GnjoC@vJw;paT1;31(_~ z|60?Vt*XsbwWO2&$&Z27^Dnm=gH)*sE(cckk(wjB1uC28)^ zvh88X`=KotDLbBVx2N6hPb*g4-78%8|FtQ}wXm%@gOUBv-enwpLt}%ZAzm1n80_2A zIOHS97J%vvmO#-5-Nm95{Z^K+o3S3JYQl=%e*?>bf~HLGzYKu2gtrn1J4?zsUbYv# z{X;@xVCgm6qZALN_ahzTnP3@hhkxya_aA z0zK(K&kE<*D;yJVGxwYcF^~z2v4jJ5ET>s1DpCL_J0w@@lfsK?Xu;*yq`NzQE+voMP^r5_6ApH;1SG$!5uwn+~p<0 z6Nn|5!6%UR6jrIjdM*U4EhsMDQ$;BTNlN~XL)M^myxpSr;=Kea&}+FwzT%v^S>m?l z3#v$U{WmlqpDb_h0MrIb4W#E38*w!|%6NH;$*l4wk+KzH?#Wz4M#y;2UeRBRF-~Bf zw%qO+lGrf2KYmjm(m;KP6u4G!!h-$^}d-V`4&$AOOcAi;8B&V4syCagEVbQ3qS^wchJ_ z&Vivp-OX#op?gs=h1o3J9*V3SA}b2jT(USVLT(X-*be)p6kdh7$UVps=QJBmrsFK0 z8m6K-pp{Re-eIp6PZ+Ly5 zu#aMTy0Bi`_IYHrvVGNi0g%w`{qRA?*PiyZuesa*?rYen3am7AuU7S}cza${H)X0j z)770T-p;J2a{21+s1OWIH^1vt3BW7(?kqqxU$5LSPg)R3a%HO0+-u2`;ft3qUIyM1 z&KGZkR-}m0q!d%Yx4P8XxKcP#Q(7i>;jbt6-O^o_5C$G40M!zC?mnDxA4$89JeD$z zo$1ETr|NoR-yd(UH(p+M53FzlyZ6Efw(vd5oM4BIWI44h)y`j+sp599q!`3;HSnS~ zRb04+1y1vYHDA6kG4E9hf+4&JFVS0R%qiHiDr{L3q6DccTsU*;N}*c|?z>0k6z5%J zKLgbgc~RzhKlIa*Wp=~iTAu$&$0O!pSJvx$bQ;pqvmf98dDptP`LnTg?-^=h%Q|oc zIo-j~lp4b!`eZ}%YWLTRV)`DB&WZ5q z6Qj1HZ%&0k6Z?2nh^X-VbbDSE6`1vaAVd=pMTxZ+Ts8gn8{RM#@2kE#0y^*h8 datetime.now() - - def enter_token(self, token, show_result=True): - if len(token) == 9: - token_int = int(token) - return self._update_device_status_from_token(token_int, show_result) - else: - token_int = int(token) - return self._update_device_status_from_extended_token(token_int, show_result) - - def get_days_remaining(self): - if self.payg_enabled: - td = self.expiration_date - datetime.now() - days, hours, minutes = td.days, td.seconds//3600, (td.seconds//60)%60 - days = days + (hours + minutes/60)/24 - return round(days) - else: - return 'infinite' - - def _update_device_status_from_token(self, token, show_result=True): - if self.token_entry_blocked_until > datetime.now() and self.waiting_period_enabled: - if show_result: - print('TOKEN_ENTRY_BLOCKED') - return False - - token_value, token_type, token_count, updated_counts = OpenPAYGOTokenDecoder.get_activation_value_count_and_type_from_token( - token=token, - starting_code=self.starting_code, - key=self.key, - last_count=self.count, - restricted_digit_set=self.restricted_digit_set, - used_counts=self.used_counts - ) - if token_value is None: - if token_type == TokenType.ALREADY_USED: - if show_result: - print('OLD_TOKEN') - return -2 - if show_result: - print('TOKEN_INVALID') - self.invalid_token_count += 1 - self.token_entry_blocked_until = datetime.now() + timedelta(minutes=2**self.invalid_token_count) - return -1 - elif token_value == -2: - if show_result: - print('OLD_TOKEN') - return -2 - else: - if show_result: - print('TOKEN_VALID', ' | Value:', token_value) - if token_count > self.count or token_value == OpenPAYGOTokenShared.COUNTER_SYNC_VALUE: - self.count = token_count - self.used_counts = updated_counts - self.invalid_token_count = 0 - self._update_device_status_from_token_value(token_value, token_type) - return 1 - - def _update_device_status_from_extended_token(self, token, show_result=True): - if self.token_entry_blocked_until > datetime.now() and self.waiting_period_enabled: - if show_result: - print('TOKEN_ENTRY_BLOCKED') - - token_value, token_count = OpenPAYGOTokenDecoder.get_activation_value_count_from_extended_token( - token=token, - starting_code=self.starting_code, - key=self.key, - last_count=self.count, - restricted_digit_set=self.restricted_digit_set - ) - if token_value is None: - if show_result: - print('TOKEN_INVALID') - self.invalid_token_count += 1 - self.token_entry_blocked_until = datetime.now() + timedelta(minutes=2**self.invalid_token_count) - else: - if show_result: - print('Special token entered, value: '+str(token_value)) - - def _update_device_status_from_token_value(self, token_value, token_type): - if token_value <= OpenPAYGOTokenShared.MAX_ACTIVATION_VALUE: - if not self.payg_enabled and token_type == TokenType.SET_TIME: - self.payg_enabled = True - if self.payg_enabled: - self._update_expiration_date_from_value(token_value, token_type) - elif token_value == OpenPAYGOTokenShared.PAYG_DISABLE_VALUE: - self.payg_enabled = False - elif token_value != OpenPAYGOTokenShared.COUNTER_SYNC_VALUE: - # We do nothing if its the sync counter value, the counter has been synced already - print('COUNTER_SYNCED') - else: - # If it's another value we also do nothing, as they are not defined - print('UNKNOWN_COMMAND') - - def _update_expiration_date_from_value(self, toke_value, token_type): - number_of_days = toke_value/self.time_divider - if token_type == TokenType.SET_TIME: - self.expiration_date = datetime.now() + timedelta(days=number_of_days) - else: - if self.expiration_date < datetime.now(): - self.expiration_date = datetime.now() - self.expiration_date = self.expiration_date + timedelta(days=number_of_days) diff --git a/simulators/server_simulator.py b/simulators/server_simulator.py deleted file mode 100644 index c1a7888..0000000 --- a/simulators/server_simulator.py +++ /dev/null @@ -1,97 +0,0 @@ -from datetime import datetime -from openpaygo.token_encode import OpenPAYGOTokenEncoder -from openpaygo.token_shared import OpenPAYGOTokenShared, TokenType - - -class SingleDeviceServerSimulator(object): - - def __init__(self, starting_code, key, starting_count=1, restricted_digit_set=False, time_divider=1): - self.starting_code = starting_code - self.key = key - self.count = starting_count - self.expiration_date = datetime.now() - self.furthest_expiration_date = datetime.now() - self.payg_enabled = True - self.time_divider = time_divider - self.restricted_digit_set = restricted_digit_set - - def print_status(self): - print('Expiration Date: '+ str(self.expiration_date)) - print('Current count: '+str(self.count)) - print('PAYG Enabled: '+str(self.payg_enabled)) - - def generate_payg_disable_token(self): - self.count, token = OpenPAYGOTokenEncoder.generate_token( - starting_code=self.starting_code, - secret_key=self.key, - value=0, - count=self.count, - token_type=TokenType.DISABLE_PAYG, - restricted_digit_set=self.restricted_digit_set - ) - return SingleDeviceServerSimulator._format_token(token) - - def generate_counter_sync_token(self): - self.count, token = OpenPAYGOTokenEncoder.generate_token( - starting_code=self.starting_code, - secret_key=self.key, - value=0, - count=self.count, - token_type=TokenType.COUNTER_SYNC, - restricted_digit_set=self.restricted_digit_set - ) - return SingleDeviceServerSimulator._format_token(token) - - def generate_token_from_date(self, new_expiration_date, force=False): - furthest_expiration_date = self.furthest_expiration_date - if new_expiration_date > self.furthest_expiration_date: - self.furthest_expiration_date = new_expiration_date - - if new_expiration_date > furthest_expiration_date: - # If the date is strictly above the furthest date activated, use ADD - value = self._get_value_to_activate(new_expiration_date, self.expiration_date, force) - self.expiration_date = new_expiration_date - return self._generate_token_from_value(value, mode=TokenType.ADD_TIME) - else: - # If the date is below or equal to the furthest date activated, use SET - value = self._get_value_to_activate(new_expiration_date, datetime.now(), force) - self.expiration_date = new_expiration_date - return self._generate_token_from_value(value, mode=TokenType.SET_TIME) - - def _generate_token_from_value(self, value, mode): - self.count, token = OpenPAYGOTokenEncoder.generate_token( - starting_code=self.starting_code, - secret_key=self.key, - value=value, - count=self.count, - token_type=mode, - restricted_digit_set=self.restricted_digit_set - ) - return SingleDeviceServerSimulator._format_token(token) - - def _generate_extended_value_token(self, value): - pass - - def _get_value_to_activate(self, new_time, reference_time, force_maximum=False): - if new_time <= reference_time: - return 0 - else: - days = self._timedelta_to_days(new_time - reference_time) - value = int(round(days*self.time_divider, 0)) - if value > OpenPAYGOTokenShared.MAX_ACTIVATION_VALUE: - if not force_maximum: - raise Exception('TOO_MANY_DAYS_TO_ACTIVATE') - else: - return OpenPAYGOTokenShared.MAX_ACTIVATION_VALUE # Will need to be activated again after those days - return value - - @staticmethod - def _timedelta_to_days(this_timedelta): - return this_timedelta.days + (this_timedelta.seconds / 3600 / 24) - - @staticmethod - def _format_token(token): - token = str(token) - if len(token) < 9: - token = '0' * (9 - len(token)) + token - return token From 5969ca4a2f86a6dd896c5bb986c9083e4dcc157c Mon Sep 17 00:00:00 2001 From: CoderOMaster Date: Tue, 23 Dec 2025 13:39:29 +0530 Subject: [PATCH 4/8] refacotring --- openpaygo/metrics_shared.py | 18 ++- openpaygo/simulators/__init__.py | 2 + openpaygo/simulators/device_simulator.py | 131 ++++++++++++++++ openpaygo/simulators/server_simulator.py | 97 ++++++++++++ openpaygo/token_decode.py | 4 +- openpaygo/token_shared.py | 19 ++- test/README.md | 10 ++ test/__init__.py | 0 test/full_test_procedure.py | 116 +++++++++++++++ test/helpers.py | 69 +++++++++ test/simple_scenario_test.py | 182 +++++++++++++++++++++++ test/spreadsheet_generator_test.py | 109 ++++++++++++++ 12 files changed, 753 insertions(+), 4 deletions(-) create mode 100644 openpaygo/simulators/__init__.py create mode 100644 openpaygo/simulators/device_simulator.py create mode 100644 openpaygo/simulators/server_simulator.py create mode 100644 test/README.md create mode 100644 test/__init__.py create mode 100644 test/full_test_procedure.py create mode 100644 test/helpers.py create mode 100644 test/simple_scenario_test.py create mode 100755 test/spreadsheet_generator_test.py diff --git a/openpaygo/metrics_shared.py b/openpaygo/metrics_shared.py index 586ab2e..cae1579 100644 --- a/openpaygo/metrics_shared.py +++ b/openpaygo/metrics_shared.py @@ -146,10 +146,26 @@ def generate_hash_string(cls, input_string, secret_key): @classmethod def load_secret_key_from_hex(cls, secret_key): + if isinstance(secret_key, (bytes, bytearray)): + secret_key_bytes = bytes(secret_key) + if len(secret_key_bytes) != 16: + raise ValueError( + "The secret key provided is not correctly formatted, it should be 16 bytes. " + ) + return secret_key_bytes + try: - return codecs.decode(secret_key, "hex") + decoded = codecs.decode(secret_key, "hex") except Exception: raise ValueError( "The secret key provided is not correctly formatted, it should be 32 " "hexadecimal characters. " ) + + if len(decoded) != 16: + raise ValueError( + "The secret key provided is not correctly formatted, it should be 32 " + "hexadecimal characters. " + ) + + return decoded diff --git a/openpaygo/simulators/__init__.py b/openpaygo/simulators/__init__.py new file mode 100644 index 0000000..618cc96 --- /dev/null +++ b/openpaygo/simulators/__init__.py @@ -0,0 +1,2 @@ +from .device_simulator import DeviceSimulator +from .server_simulator import SingleDeviceServerSimulator \ No newline at end of file diff --git a/openpaygo/simulators/device_simulator.py b/openpaygo/simulators/device_simulator.py new file mode 100644 index 0000000..5468170 --- /dev/null +++ b/openpaygo/simulators/device_simulator.py @@ -0,0 +1,131 @@ +from datetime import datetime, timedelta +from openpaygo.token_shared import OpenPAYGOTokenShared +from openpaygo.token_decode import OpenPAYGOTokenDecoder, TokenType + + +class DeviceSimulator(object): + + def __init__(self, starting_code, key, starting_count=1, restricted_digit_set=False, waiting_period_enabled=True, time_divider=1): + self.starting_code = starting_code + self.key = key + self.time_divider = time_divider + self.restricted_digit_set = restricted_digit_set + self.waiting_period_enabled = waiting_period_enabled # Should always be true except for testing + + self.payg_enabled = True + self.count = starting_count + self.expiration_date = datetime.now() + self.invalid_token_count = 0 + self.token_entry_blocked_until = datetime.now() + self.used_counts = [] + + def print_status(self): + print('-------------------------') + print('Expiration Date: '+ str(self.expiration_date)) + print('Current count: '+str(self.count)) + print('PAYG Enabled: '+str(self.payg_enabled)) + print('Active: '+str(self.is_active())) + print('-------------------------') + + def is_active(self): + return self.expiration_date > datetime.now() + + def enter_token(self, token, show_result=True): + if len(token) == 9: + token_int = int(token) + return self._update_device_status_from_token(token_int, show_result) + else: + token_int = int(token) + return self._update_device_status_from_extended_token(token_int, show_result) + + def get_days_remaining(self): + if self.payg_enabled: + td = self.expiration_date - datetime.now() + days, hours, minutes = td.days, td.seconds//3600, (td.seconds//60)%60 + days = days + (hours + minutes/60)/24 + return round(days) + else: + return 'infinite' + + def _update_device_status_from_token(self, token, show_result=True): + if self.token_entry_blocked_until > datetime.now() and self.waiting_period_enabled: + if show_result: + print('TOKEN_ENTRY_BLOCKED') + return False + + token_value, token_type, token_count, updated_counts = OpenPAYGOTokenDecoder.get_activation_value_count_and_type_from_token( + token=token, + starting_code=self.starting_code, + key=self.key, + last_count=self.count, + restricted_digit_set=self.restricted_digit_set, + used_counts=self.used_counts + ) + if token_value is None: + if token_type == TokenType.ALREADY_USED: + if show_result: + print('OLD_TOKEN') + return -2 + if show_result: + print('TOKEN_INVALID') + self.invalid_token_count += 1 + self.token_entry_blocked_until = datetime.now() + timedelta(minutes=2**self.invalid_token_count) + return -1 + elif token_value == -2: + if show_result: + print('OLD_TOKEN') + return -2 + else: + if show_result: + print('TOKEN_VALID', ' | Value:', token_value) + if token_count > self.count or token_value == OpenPAYGOTokenShared.COUNTER_SYNC_VALUE: + self.count = token_count + self.used_counts = updated_counts + self.invalid_token_count = 0 + self._update_device_status_from_token_value(token_value, token_type) + return 1 + + def _update_device_status_from_extended_token(self, token, show_result=True): + if self.token_entry_blocked_until > datetime.now() and self.waiting_period_enabled: + if show_result: + print('TOKEN_ENTRY_BLOCKED') + + token_value, token_count = OpenPAYGOTokenDecoder.get_activation_value_count_from_extended_token( + token=token, + starting_code=self.starting_code, + key=self.key, + last_count=self.count, + restricted_digit_set=self.restricted_digit_set + ) + if token_value is None: + if show_result: + print('TOKEN_INVALID') + self.invalid_token_count += 1 + self.token_entry_blocked_until = datetime.now() + timedelta(minutes=2**self.invalid_token_count) + else: + if show_result: + print('Special token entered, value: '+str(token_value)) + + def _update_device_status_from_token_value(self, token_value, token_type): + if token_value <= OpenPAYGOTokenShared.MAX_ACTIVATION_VALUE: + if not self.payg_enabled and token_type == TokenType.SET_TIME: + self.payg_enabled = True + if self.payg_enabled: + self._update_expiration_date_from_value(token_value, token_type) + elif token_value == OpenPAYGOTokenShared.PAYG_DISABLE_VALUE: + self.payg_enabled = False + elif token_value != OpenPAYGOTokenShared.COUNTER_SYNC_VALUE: + # We do nothing if its the sync counter value, the counter has been synced already + print('COUNTER_SYNCED') + else: + # If it's another value we also do nothing, as they are not defined + print('UNKNOWN_COMMAND') + + def _update_expiration_date_from_value(self, toke_value, token_type): + number_of_days = toke_value/self.time_divider + if token_type == TokenType.SET_TIME: + self.expiration_date = datetime.now() + timedelta(days=number_of_days) + else: + if self.expiration_date < datetime.now(): + self.expiration_date = datetime.now() + self.expiration_date = self.expiration_date + timedelta(days=number_of_days) diff --git a/openpaygo/simulators/server_simulator.py b/openpaygo/simulators/server_simulator.py new file mode 100644 index 0000000..c1a7888 --- /dev/null +++ b/openpaygo/simulators/server_simulator.py @@ -0,0 +1,97 @@ +from datetime import datetime +from openpaygo.token_encode import OpenPAYGOTokenEncoder +from openpaygo.token_shared import OpenPAYGOTokenShared, TokenType + + +class SingleDeviceServerSimulator(object): + + def __init__(self, starting_code, key, starting_count=1, restricted_digit_set=False, time_divider=1): + self.starting_code = starting_code + self.key = key + self.count = starting_count + self.expiration_date = datetime.now() + self.furthest_expiration_date = datetime.now() + self.payg_enabled = True + self.time_divider = time_divider + self.restricted_digit_set = restricted_digit_set + + def print_status(self): + print('Expiration Date: '+ str(self.expiration_date)) + print('Current count: '+str(self.count)) + print('PAYG Enabled: '+str(self.payg_enabled)) + + def generate_payg_disable_token(self): + self.count, token = OpenPAYGOTokenEncoder.generate_token( + starting_code=self.starting_code, + secret_key=self.key, + value=0, + count=self.count, + token_type=TokenType.DISABLE_PAYG, + restricted_digit_set=self.restricted_digit_set + ) + return SingleDeviceServerSimulator._format_token(token) + + def generate_counter_sync_token(self): + self.count, token = OpenPAYGOTokenEncoder.generate_token( + starting_code=self.starting_code, + secret_key=self.key, + value=0, + count=self.count, + token_type=TokenType.COUNTER_SYNC, + restricted_digit_set=self.restricted_digit_set + ) + return SingleDeviceServerSimulator._format_token(token) + + def generate_token_from_date(self, new_expiration_date, force=False): + furthest_expiration_date = self.furthest_expiration_date + if new_expiration_date > self.furthest_expiration_date: + self.furthest_expiration_date = new_expiration_date + + if new_expiration_date > furthest_expiration_date: + # If the date is strictly above the furthest date activated, use ADD + value = self._get_value_to_activate(new_expiration_date, self.expiration_date, force) + self.expiration_date = new_expiration_date + return self._generate_token_from_value(value, mode=TokenType.ADD_TIME) + else: + # If the date is below or equal to the furthest date activated, use SET + value = self._get_value_to_activate(new_expiration_date, datetime.now(), force) + self.expiration_date = new_expiration_date + return self._generate_token_from_value(value, mode=TokenType.SET_TIME) + + def _generate_token_from_value(self, value, mode): + self.count, token = OpenPAYGOTokenEncoder.generate_token( + starting_code=self.starting_code, + secret_key=self.key, + value=value, + count=self.count, + token_type=mode, + restricted_digit_set=self.restricted_digit_set + ) + return SingleDeviceServerSimulator._format_token(token) + + def _generate_extended_value_token(self, value): + pass + + def _get_value_to_activate(self, new_time, reference_time, force_maximum=False): + if new_time <= reference_time: + return 0 + else: + days = self._timedelta_to_days(new_time - reference_time) + value = int(round(days*self.time_divider, 0)) + if value > OpenPAYGOTokenShared.MAX_ACTIVATION_VALUE: + if not force_maximum: + raise Exception('TOO_MANY_DAYS_TO_ACTIVATE') + else: + return OpenPAYGOTokenShared.MAX_ACTIVATION_VALUE # Will need to be activated again after those days + return value + + @staticmethod + def _timedelta_to_days(this_timedelta): + return this_timedelta.days + (this_timedelta.seconds / 3600 / 24) + + @staticmethod + def _format_token(token): + token = str(token) + if len(token) < 9: + token = '0' * (9 - len(token)) + token + return token diff --git a/openpaygo/token_decode.py b/openpaygo/token_decode.py index 5a389bb..b813fa0 100644 --- a/openpaygo/token_decode.py +++ b/openpaygo/token_decode.py @@ -134,6 +134,8 @@ def get_activation_value_count_and_type_from_token( @classmethod def _count_is_valid(cls, count, last_count, value, type, used_counts): + if used_counts is None: + used_counts = [] if value == OpenPAYGOTokenShared.COUNTER_SYNC_VALUE: if count > (last_count - cls.MAX_TOKEN_JUMP): return True @@ -147,7 +149,7 @@ def _count_is_valid(cls, count, last_count, value, type, used_counts): @classmethod def update_used_counts(cls, past_used_counts, value, new_count, type): - if not past_used_counts: + if past_used_counts is None: return None highest_count = max(past_used_counts) if past_used_counts else 0 if new_count > highest_count: diff --git a/openpaygo/token_shared.py b/openpaygo/token_shared.py index 2529b91..f6f0bca 100644 --- a/openpaygo/token_shared.py +++ b/openpaygo/token_shared.py @@ -1,6 +1,5 @@ import codecs import struct - import siphash @@ -61,14 +60,30 @@ def generate_starting_code(cls, key): @classmethod def load_secret_key_from_hex(cls, secret_key): + if isinstance(secret_key, (bytes, bytearray)): + secret_key_bytes = bytes(secret_key) + if len(secret_key_bytes) != 16: + raise ValueError( + "The secret key provided is not correctly formatted, it should be 16 bytes. " + ) + return secret_key_bytes + try: - return codecs.decode(secret_key, "hex") + decoded = codecs.decode(secret_key, "hex") except Exception: raise ValueError( "The secret key provided is not correctly formatted, it should be 32 " "hexadecimal characters. " ) + if len(decoded) != 16: + raise ValueError( + "The secret key provided is not correctly formatted, it should be 32 " + "hexadecimal characters. " + ) + + return decoded + @classmethod def _convert_to_29_5_bits(cls, source): mask = ((1 << (32 - 2 + 1)) - 1) << 2 diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..8f15c85 --- /dev/null +++ b/test/README.md @@ -0,0 +1,10 @@ +# OpenPAYGO-Token Test + +To run the test, first, create and activate a virtual environment. Then install the module and run the test: + +``` +$ cd tests +$ python -m venv env +$ python -m pip install -e .. +$ python simple_scenario_test.py +``` diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/full_test_procedure.py b/test/full_test_procedure.py new file mode 100644 index 0000000..af33cd5 --- /dev/null +++ b/test/full_test_procedure.py @@ -0,0 +1,116 @@ +from helpers import ADD_TIME, SET_TIME, DISABLE_VALUE, generate_from_device_data, test_accepted_validator, test_how_many_days_validator, test_name +import codecs + +from openpaygo.simulators.device_simulator import DeviceSimulator +from openpaygo.simulators.server_simulator import SingleDeviceServerSimulator + +# This tests the device simulator against the full test procedure + + +def run_core_token_tests(device_data, device_simulator): + + test = 'We enter an invalid token' + token_g1 = '123 456 789' + test_accepted_validator(device_simulator, 'G1', token_g1, -1, description=test) + + test = 'We enter a valid token for setting one day' + device_data, token_g2 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=1) + test_how_many_days_validator(device_simulator, 'G2', token_g2, 1, description=test) + + test = 'We enter a valid token for adding one day' + device_data, token_g3 = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) + test_how_many_days_validator(device_simulator, 'G3', token_g3, 2, description=test) + + test = 'We enter the same Add Time token for 1 day, the days should not be added and the device should signal that the token was already used' + test_how_many_days_validator(device_simulator, 'G4A', token_g3, 2, description=test, accepted_but_used=True) + test = 'We enter the older Set Time token for 1 day, the days should not change and the device should signal that the token was already used' + test_how_many_days_validator(device_simulator, 'G4B', token_g2, 2, description=test, accepted_but_used=True) + + test = 'We enter a valid token for setting 30 days and ensures it sets and does not add to the existing' + device_data, token_g5 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=30) + test_how_many_days_validator(device_simulator, 'G5', token_g5, 30, description=test) + + test = 'We enter a valid token for setting 0 days and ensures the device is inactive with the outputs disabled immediately' + device_data, token_g6 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) + test_how_many_days_validator(device_simulator, 'G6', token_g6, 0, description=test) + + test = 'We enter 3 consecutive Add Time tokens with the maximum amount of days and ensure that they cumulate properly' + for i in range(1,3+1): + device_data, token_g7 = generate_from_device_data(device_data, token_type=ADD_TIME, value_raw=995) + test_how_many_days_validator(device_simulator, test_name('G7', i), token_g7, value_raw=995*i, device_data=device_data, description=test) + test = '' + + test = 'We enter 21 consecutive Set Time tokens for 1, 2, 3, … 21 days each with a count 30 higher than the other. The validation of the token should not take more than 5 seconds' + for i in range(1,21+1): + device_data, token_g8 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=i, token_count=device_data['token_count']+29) + test_how_many_days_validator(device_simulator, test_name('G8', i), token_g8, value_days=i, device_data=device_data, description=test) + test = '' + + test = 'We enter a PAYG Disable token into the device' + device_data, token_g9 = generate_from_device_data(device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE) + test_how_many_days_validator(device_simulator, 'G9', token_g9, None, description=test) + + test = 'We enter a Set Time token for 0 day, it should relock the device' + device_data, token_g10 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) + test_how_many_days_validator(device_simulator, 'G10', token_g10, 0, description=test) + + test = 'We enter a PAYG Disable token to relock the device, then enter a Add Time token with 0 day, it should NOT relock the device (Optional)' + device_data, token_g11a = generate_from_device_data(device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE) + test_how_many_days_validator(device_simulator, 'G11A', token_g11a, None, description=test) + device_data, token_g11b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=0) + test_how_many_days_validator(device_simulator, 'G11B', token_g11b, None) + + test = 'We deactivate the device with a Set Time of 0 days. We then wait 48 hours before entering a Add Time of 1 day and ensuring that the days late are not considered in the activation time' + device_data, token_g12a = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) + test_how_many_days_validator(device_simulator, 'G12A', token_g12a, 0, description=test) + device_data, token_g12b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) + test_how_many_days_validator(device_simulator, 'G12B', token_g12b, 1) + + return device_data + + +def run_unordered_entry_tests(device_data, device_simulator): + + test = 'We generate 3 Add Time tokens, then enter the 3rd, then first, then second and ensure the days are added properly' + device_data, token_u1a = generate_from_device_data(device_data, token_type=SET_TIME, value_days=60) + test_how_many_days_validator(device_simulator, 'U1A', token_u1a, 60, description=test) + device_data, token_u1b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=10) + device_data, token_u1c = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=5) + device_data, token_u1d = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) + test_how_many_days_validator(device_simulator, 'U1B', token_u1d, 61) + test_how_many_days_validator(device_simulator, 'U1C', token_u1b, 71) + test_how_many_days_validator(device_simulator, 'U1D', token_u1c, 76) + + test = 'We generate an Add Time, a Set Time and another Add Time token. We enter the set time and ensure that the older Add Time does not work but the newer does' + device_data, token_u2a = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=5) + device_data, token_u2b = generate_from_device_data(device_data, token_type=SET_TIME, value_days=10) + device_data, token_u2c = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=3) + test_how_many_days_validator(device_simulator, 'U2A', token_u2b, 10, description=test) + test_how_many_days_validator(device_simulator, 'U2B', token_u2a, 10, accepted_but_used=True) + test_how_many_days_validator(device_simulator, 'U2C', token_u2c, 13) + + test = 'We generate an Add Time token and a Disable PAYG token, we enter the Disable PAYG token and then the Add Time token should be refused' + device_data, token_u3a = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) + device_data, token_u3b = generate_from_device_data(device_data, token_type=ADD_TIME, value_raw=DISABLE_VALUE) + test_how_many_days_validator(device_simulator, 'U3A', token_u3b, None, description=test) + test_how_many_days_validator(device_simulator, 'U3B', token_u3a, None, accepted_but_used=True) + + return device_data + + + + +if __name__ == '__main__': + device_data = { + 'serial_number': 'XXX', + 'starting_code': 516959010, + 'key': 'bc41ec9530f6dac86b1a29ab82edc5fb', + 'restricted_digit_set': False, + 'time_divider': 1, + 'token_count': 1 + } + device_simulator = DeviceSimulator(device_data['starting_code'], codecs.decode(device_data['key'], 'hex'), + restricted_digit_set=device_data['restricted_digit_set'], + waiting_period_enabled=False) + device_data = run_core_token_tests(device_data, device_simulator) + device_data = run_unordered_entry_tests(device_data, device_simulator) diff --git a/test/helpers.py b/test/helpers.py new file mode 100644 index 0000000..1cd2029 --- /dev/null +++ b/test/helpers.py @@ -0,0 +1,69 @@ +import codecs +import openpaygo + +from openpaygo.token_shared import TokenType + + +SET_TIME = TokenType.SET_TIME +ADD_TIME = TokenType.ADD_TIME +DISABLE_VALUE = 998 + +START_RED = '\033[91m' +END_RED = '\033[0m' + + +def generate_from_device_data(device_data, token_type, value_raw=None, value_days=None, token_count=None): + assert (value_days is not None) or (value_raw is not None) + if value_raw is None: + value_raw = value_days*device_data['time_divider'] + device_data['token_count'], token = openpaygo.token_encode.OpenPAYGOTokenEncoder.generate_standard_token( + starting_code=device_data['starting_code'], + key=codecs.decode(device_data['key'], 'hex'), + value=value_raw, + count=token_count or device_data['token_count'], + restricted_digit_set=device_data['restricted_digit_set'], + mode=token_type + ) + token = str(token).rjust(9, '0') + token = ' '.join([token[i:i + 3] for i in range(0, len(token), 3)]) + return device_data, token + + +def test_how_many_days(test_name, token, value_days=None, value_raw=None, device_data=None, description=''): + if value_days is None: + if value_raw is not None: + value_days = value_raw/device_data['time_divider'] + else: + value_days = 'infinite' + print(test_name+','+token+','+str(value_days)+' days active'+',"'+description+'"') + + +def test_accepted(test_name, token, expected, description=''): + expected_string = 'Token Accepted' if expected else 'Token Refused' + print(test_name+','+token+','+expected_string+',"'+description+'"') + + +def test_how_many_days_validator(device_simulator, test_name, token, value_days=None, value_raw=None, device_data=None, description='', accepted_but_used=False): + if value_days is None: + if value_raw is not None: + value_days = value_raw/device_data['time_divider'] + else: + value_days = 'infinite' + result = device_simulator.enter_token(token.replace(' ', ''), show_result=False) + if result == 1 or (accepted_but_used and result == -2): + if device_simulator.get_days_remaining() == value_days: + print(test_name+': Passed') + return + print(START_RED+test_name+': Failed'+END_RED) + + +def test_accepted_validator(device_simulator, test_name, token, expected, description=''): + if device_simulator.enter_token(token.replace(' ', ''), show_result=False) == expected: + print(test_name+': Passed') + return + else: + print(START_RED+test_name+': Failed'+END_RED) + + +def test_name(test_base_name, test_number): + return test_base_name+chr(test_number+64) diff --git a/test/simple_scenario_test.py b/test/simple_scenario_test.py new file mode 100644 index 0000000..59ab763 --- /dev/null +++ b/test/simple_scenario_test.py @@ -0,0 +1,182 @@ +from datetime import datetime, timedelta +from openpaygo.simulators import DeviceSimulator, SingleDeviceServerSimulator +from openpaygo.token_shared import TokenType + + +def assert_time_equals(time1, time2): + assert time1.replace(second=0, microsecond=0) == time2.replace(second=0, microsecond=0) + + +if __name__ == '__main__': + # ------ IMPORTANT WARNING -------- + # DO NOT USE THIS KEY IN PRODUCTION + # IT IS JUST AN EXAMPLE + # --------------------------------- + device_key = b'\xa2\x9a\xb8.\xdc_\xbb\xc4\x1e\xc9S\x0fm\xac\x86\xb1' + device_starting_code = 123456789 # Generated by fair dice roll + restricted_digit_set = False + + print('Device: We initiate the device simulator with our device') + device_simulator = DeviceSimulator(device_starting_code, device_key, + restricted_digit_set=restricted_digit_set, + waiting_period_enabled=False) + print('Server: We initiate the server simulator with our device') + server_simulator = SingleDeviceServerSimulator(device_starting_code, device_key, + restricted_digit_set=restricted_digit_set) + + print('\n') + print('Device: We try entering an invalid token into the device: 123456789') + device_simulator.enter_token('123456789') + print('Device: We check the device status (should be still inactive)') + device_simulator.print_status() + + print('\n') + print('Server: We add 1 days of activation for the device') + this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) + print('Token: '+this_token) + print('Device: We enter the generated token into the device') + device_simulator.enter_token(this_token) + print('Device: We check the device status (should be active with 1 day)') + device_simulator.print_status() + assert device_simulator.count == server_simulator.count + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1)) + + print('\n') + print('Device: We enter the token a second time to make sure it doesnt add the days again') + device_simulator.enter_token(this_token) + print('Device: We check the device status (should be active with 1 day)') + device_simulator.print_status() + assert device_simulator.count == server_simulator.count + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1)) + + print('\n') + print('Server: We set it to expire in 30 days') + this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=30)) + print('Token: ' + this_token) + print('Device: We enter the generated token into the device') + device_simulator.enter_token(this_token) + print('Device: We check the device status (should be active with 30 days)') + device_simulator.print_status() + assert device_simulator.count == server_simulator.count + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=30)) + + print('\n') + print('Server: We set it to expire in 7 days (removing 23 days)') + this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=7)) + print('Token: ' + this_token) + print('Device: We enter the generated token into the device') + device_simulator.enter_token(this_token) + print('Device: We check the device status (should be active with 7 days)') + device_simulator.print_status() + assert device_simulator.count == server_simulator.count + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=7)) + + print('\n') + print('Server: We generate a token for putting the device in PAYG-OFF mode') + this_PAYG_OFF_code = server_simulator.generate_payg_disable_token() + this_PAYG_OFF_code = str(this_PAYG_OFF_code) + print('Token: ' + this_PAYG_OFF_code) + print('Device: We enter the generated token into the device') + device_simulator.enter_token(this_PAYG_OFF_code) + print('Device: We check the device status (should be active forver)') + device_simulator.print_status() + assert device_simulator.count == server_simulator.count + assert device_simulator.payg_enabled is False + + print('\n') + print('Server: We generate a token for putting the device back PAYG-ON mode with 0 days') + this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=0)) + this_token = str(this_token) + print('Token: ' + this_token) + print('Device: We enter the generated token into the device') + device_simulator.enter_token(this_token) + print('Device: We check the device status (should not be active)') + device_simulator.print_status() + assert device_simulator.count == server_simulator.count + assert device_simulator.payg_enabled is True + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=0)) + + print('\n') + print('Server: We generate a bunch of 1 day tokens, but only enter the latest one') + server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) + server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) + server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) + server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) + server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) + this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) + this_token = str(this_token) + print('Token: ' + this_token) + print('Device: We enter the latest generated token into the device') + device_simulator.enter_token(this_token) + print('Device: We check the device status (should be active with 1 day and the count synchronised with the server)') + device_simulator.print_status() + assert device_simulator.count == server_simulator.count + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1)) + + print('\n') + print('Server: We add generate 9 tokens each add-time of 1 day') + token_1 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) + token_2 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) + token_3 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) + token_4 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) + token_5 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) + token_6 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) + token_7 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) + token_8 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) + token_9 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) + print('Tokens: ', token_1, token_2, token_3, token_4, token_5, token_6, token_7, token_8, token_9) + print('Device: We enter the 9th token into the device') + device_simulator.enter_token(token_9) + print('Device: We check the device status (should be active with +1 day (2 days total)') + device_simulator.print_status() + assert device_simulator.count == server_simulator.count + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=2)) + print('Device: We enter the 1st token into the device') + device_simulator.enter_token(token_1) + print('Device: We check the device status , it should not have changed, because its more than 5 add times before') + device_simulator.print_status() + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=2)) + print('Device: We enter the tokens 5, 4, 3 and 2 into the device') + device_simulator.enter_token(token_5) + device_simulator.enter_token(token_4) + device_simulator.enter_token(token_3) + device_simulator.enter_token(token_2) + print('Device: We check the device status , it should have +4 days (6 days total)') + device_simulator.print_status() + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=6)) + + print('\n') + print('Server: We add generate 2 tokens, first add-time and then set-time') + token_1 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) + token_2 = server_simulator._generate_token_from_value(0, TokenType.SET_TIME) + print('Tokens: ', token_1, token_2) + print('Device: We enter the 2nd token') + device_simulator.enter_token(token_2) + print('Device: We check the device status should be active in 0 days.') + device_simulator.print_status() + assert device_simulator.count == server_simulator.count + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=0)) + print('Device: We enter the 1st token into the device') + device_simulator.enter_token(token_1) + print('Device: We check the device status , it should not have changed, ' + 'because you cannot use an add-time token older than a set-time') + device_simulator.print_status() + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=0)) + + print('\n') + print('Server: We add generate 2 tokens, first add-time and then set-time') + token_1 = server_simulator._generate_token_from_value(1, TokenType.SET_TIME) + token_2 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) + print('Tokens: ', token_1, token_2) + print('Device: We enter the 2nd token') + device_simulator.enter_token(token_2) + print('Device: We check the device status should be active in 0 days.') + device_simulator.print_status() + assert device_simulator.count == server_simulator.count + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1)) + print('Device: We enter the 1st token into the device') + device_simulator.enter_token(token_1) + print('Device: We check the device status , it should not have changed, ' + 'because you cannot use an older set-time token') + device_simulator.print_status() + assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1)) diff --git a/test/spreadsheet_generator_test.py b/test/spreadsheet_generator_test.py new file mode 100755 index 0000000..be23d08 --- /dev/null +++ b/test/spreadsheet_generator_test.py @@ -0,0 +1,109 @@ +from test.helpers import ADD_TIME, SET_TIME, DISABLE_VALUE, generate_from_device_data, test_accepted, test_how_many_days, test_name +__test__ = False + + +def run_core_token_tests(device_data): + + test = 'We enter an invalid token' + token_g1 = '123 456 789' + test_accepted('G1', token_g1, False, description=test) + + test = 'We enter a valid token for setting one day' + device_data, token_g2 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=1) + test_how_many_days('G2', token_g2, 1, description=test) + + test = 'We enter a valid token for adding one day' + device_data, token_g3 = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) + test_how_many_days('G3', token_g3, 2, description=test) + + test = 'We enter the same Add Time token for 1 day, the days should not be added and the device should signal that the token was already used' + test_how_many_days('G4A', token_g3, 2, description=test) + test = 'We enter the older Set Time token for 1 day, the days should not change and the device should signal that the token was already used' + test_how_many_days('G4B', token_g2, 2, description=test) + + test = 'We enter a valid token for setting 30 days and ensures it sets and does not add to the existing' + device_data, token_g5 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=30) + test_how_many_days('G5', token_g5, 30, description=test) + + test = 'We enter a valid token for setting 0 days and ensures the device is inactive with the outputs disabled immediately' + device_data, token_g6 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) + test_how_many_days('G6', token_g6, 0, description=test) + + test = 'We enter 3 consecutive Add Time tokens with the maximum amount of days and ensure that they cumulate properly' + for i in range(1,3+1): + device_data, token_g7 = generate_from_device_data(device_data, token_type=ADD_TIME, value_raw=995) + test_how_many_days(test_name('G7', i), token_g7, value_raw=995*i, device_data=device_data, description=test) + test = '' + + test = 'We enter 21 consecutive Set Time tokens for 1, 2, 3, … 21 days each with a count 30 higher than the other. The validation of the token should not take more than 5 seconds' + for i in range(1,21+1): + device_data, token_g8 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=i, token_count=device_data['token_count']+29) + test_how_many_days(test_name('G8', i), token_g8, value_days=i, device_data=device_data, description=test) + test = '' + + test = 'We enter a PAYG Disable token into the device' + device_data, token_g9 = generate_from_device_data(device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE) + test_how_many_days('G9', token_g9, None, description=test) + + test = 'We enter a Set Time token for 0 day, it should relock the device' + device_data, token_g10 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) + test_how_many_days('G10', token_g10, 0, description=test) + + test = 'We enter a PAYG Disable token to relock the device, then enter a Add Time token with 0 day, it should NOT relock the device (Optional)' + device_data, token_g11a = generate_from_device_data(device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE) + test_how_many_days('G11A', token_g11a, None, description=test) + device_data, token_g11b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=0) + test_how_many_days('G11B', token_g11b, None) + + test = 'We deactivate the device with a Set Time of 0 days. We then wait 48 hours before entering a Add Time of 1 day and ensuring that the days late are not considered in the activation time' + device_data, token_g12a = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) + test_how_many_days('G12A', token_g12a, 0, description=test) + device_data, token_g12b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) + test_how_many_days('G12B', token_g12b, 1) + + return device_data + + +def run_unordered_entry_tests(device_data): + + test = 'We generate 3 Add Time tokens, then enter the 3rd, then first, then second and ensure the days are added properly' + device_data, token_u1a = generate_from_device_data(device_data, token_type=SET_TIME, value_days=60) + test_how_many_days('U1A', token_u1a, 60, description=test) + device_data, token_u1b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=10) + device_data, token_u1c = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=5) + device_data, token_u1d = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) + test_how_many_days('U1B', token_u1d, 61) + test_how_many_days('U1C', token_u1b, 71) + test_how_many_days('U1D', token_u1c, 76) + + test = 'We generate an Add Time, a Set Time and another Add Time token. We enter the set time and ensure that the older Add Time does not work but the newer does' + device_data, token_u2a = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=5) + device_data, token_u2b = generate_from_device_data(device_data, token_type=SET_TIME, value_days=10) + device_data, token_u2c = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=3) + test_how_many_days('U2A', token_u2b, 10, description=test) + test_accepted('U2B', token_u2a, False) + test_how_many_days('U2C', token_u2c, 13) + + test = 'We generate an Add Time token and a Disable PAYG token, we enter the Disable PAYG token and then the Add Time token should be refused' + device_data, token_u3a = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) + device_data, token_u3b = generate_from_device_data(device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE) + test_how_many_days('U3A', token_u3b, None, description=test) + test_accepted('U3B', token_u3a, False) + + return device_data + + + + +if __name__ == '__main__': + device_data = { + 'serial_number': 'changeme', + 'starting_code': 123456789, + 'key': 'a29ab82edc5fbbc41ec9530f6dac86b1', + 'restricted_digit_set': False, + 'time_divider': 1, + 'token_count': 1 + } + print('Test Name,Token Used,Expected Result,Test Details,Result') + device_data = run_core_token_tests(device_data) + device_data = run_unordered_entry_tests(device_data) From b982f470db98e2e25235f7804f50016819c5ff9d Mon Sep 17 00:00:00 2001 From: Keshav Arora Date: Tue, 23 Dec 2025 13:44:21 +0530 Subject: [PATCH 5/8] readme updated --- test/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/test/README.md b/test/README.md index 8f15c85..c818da0 100644 --- a/test/README.md +++ b/test/README.md @@ -5,6 +5,7 @@ To run the test, first, create and activate a virtual environment. Then install ``` $ cd tests $ python -m venv env +$ source env/bin/activate $ python -m pip install -e .. $ python simple_scenario_test.py ``` From 90c9b2a6816c1b72eaebdba3a5ef9e75a372479a Mon Sep 17 00:00:00 2001 From: CoderOMaster Date: Tue, 23 Dec 2025 23:29:03 +0530 Subject: [PATCH 6/8] lint-ed changes --- test/full_test_procedure.py | 324 ++++++++++++++++++++--------- test/helpers.py | 95 ++++++--- test/simple_scenario_test.py | 269 +++++++++++++++--------- test/spreadsheet_generator_test.py | 282 +++++++++++++++++-------- 4 files changed, 654 insertions(+), 316 deletions(-) diff --git a/test/full_test_procedure.py b/test/full_test_procedure.py index af33cd5..5cbcc7b 100644 --- a/test/full_test_procedure.py +++ b/test/full_test_procedure.py @@ -1,116 +1,250 @@ -from helpers import ADD_TIME, SET_TIME, DISABLE_VALUE, generate_from_device_data, test_accepted_validator, test_how_many_days_validator, test_name import codecs +from helpers import ( + ADD_TIME, + DISABLE_VALUE, + SET_TIME, + generate_from_device_data, + test_accepted_validator, + test_how_many_days_validator, + test_name, +) + from openpaygo.simulators.device_simulator import DeviceSimulator -from openpaygo.simulators.server_simulator import SingleDeviceServerSimulator # This tests the device simulator against the full test procedure def run_core_token_tests(device_data, device_simulator): - - test = 'We enter an invalid token' - token_g1 = '123 456 789' - test_accepted_validator(device_simulator, 'G1', token_g1, -1, description=test) - - test = 'We enter a valid token for setting one day' - device_data, token_g2 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=1) - test_how_many_days_validator(device_simulator, 'G2', token_g2, 1, description=test) - - test = 'We enter a valid token for adding one day' - device_data, token_g3 = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) - test_how_many_days_validator(device_simulator, 'G3', token_g3, 2, description=test) - - test = 'We enter the same Add Time token for 1 day, the days should not be added and the device should signal that the token was already used' - test_how_many_days_validator(device_simulator, 'G4A', token_g3, 2, description=test, accepted_but_used=True) - test = 'We enter the older Set Time token for 1 day, the days should not change and the device should signal that the token was already used' - test_how_many_days_validator(device_simulator, 'G4B', token_g2, 2, description=test, accepted_but_used=True) - - test = 'We enter a valid token for setting 30 days and ensures it sets and does not add to the existing' - device_data, token_g5 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=30) - test_how_many_days_validator(device_simulator, 'G5', token_g5, 30, description=test) - - test = 'We enter a valid token for setting 0 days and ensures the device is inactive with the outputs disabled immediately' - device_data, token_g6 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) - test_how_many_days_validator(device_simulator, 'G6', token_g6, 0, description=test) - - test = 'We enter 3 consecutive Add Time tokens with the maximum amount of days and ensure that they cumulate properly' - for i in range(1,3+1): - device_data, token_g7 = generate_from_device_data(device_data, token_type=ADD_TIME, value_raw=995) - test_how_many_days_validator(device_simulator, test_name('G7', i), token_g7, value_raw=995*i, device_data=device_data, description=test) - test = '' - - test = 'We enter 21 consecutive Set Time tokens for 1, 2, 3, … 21 days each with a count 30 higher than the other. The validation of the token should not take more than 5 seconds' - for i in range(1,21+1): - device_data, token_g8 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=i, token_count=device_data['token_count']+29) - test_how_many_days_validator(device_simulator, test_name('G8', i), token_g8, value_days=i, device_data=device_data, description=test) - test = '' - - test = 'We enter a PAYG Disable token into the device' - device_data, token_g9 = generate_from_device_data(device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE) - test_how_many_days_validator(device_simulator, 'G9', token_g9, None, description=test) - - test = 'We enter a Set Time token for 0 day, it should relock the device' - device_data, token_g10 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) - test_how_many_days_validator(device_simulator, 'G10', token_g10, 0, description=test) - - test = 'We enter a PAYG Disable token to relock the device, then enter a Add Time token with 0 day, it should NOT relock the device (Optional)' - device_data, token_g11a = generate_from_device_data(device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE) - test_how_many_days_validator(device_simulator, 'G11A', token_g11a, None, description=test) - device_data, token_g11b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=0) - test_how_many_days_validator(device_simulator, 'G11B', token_g11b, None) - - test = 'We deactivate the device with a Set Time of 0 days. We then wait 48 hours before entering a Add Time of 1 day and ensuring that the days late are not considered in the activation time' - device_data, token_g12a = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) - test_how_many_days_validator(device_simulator, 'G12A', token_g12a, 0, description=test) - device_data, token_g12b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) - test_how_many_days_validator(device_simulator, 'G12B', token_g12b, 1) + test = "We enter an invalid token" + token_g1 = "123 456 789" + test_accepted_validator(device_simulator, "G1", token_g1, -1, description=test) + + test = "We enter a valid token for setting one day" + device_data, token_g2 = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=1 + ) + test_how_many_days_validator(device_simulator, "G2", token_g2, 1, description=test) + + test = "We enter a valid token for adding one day" + device_data, token_g3 = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=1 + ) + test_how_many_days_validator(device_simulator, "G3", token_g3, 2, description=test) + + test = ( + "We enter the same Add Time token for 1 day, the days should not be " + "added and the device should signal that the token was already used" + ) + test_how_many_days_validator( + device_simulator, + "G4A", + token_g3, + 2, + description=test, + accepted_but_used=True, + ) + test = ( + "We enter the older Set Time token for 1 day, the days should not " + "change and the device should signal that the token was already used" + ) + test_how_many_days_validator( + device_simulator, + "G4B", + token_g2, + 2, + description=test, + accepted_but_used=True, + ) + + test = ( + "We enter a valid token for setting 30 days and ensures it sets and does " + "not add to the existing" + ) + device_data, token_g5 = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=30 + ) + test_how_many_days_validator(device_simulator, "G5", token_g5, 30, description=test) + + test = ( + "We enter a valid token for setting 0 days and ensures the device is " + "inactive with the outputs disabled immediately" + ) + device_data, token_g6 = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=0 + ) + test_how_many_days_validator(device_simulator, "G6", token_g6, 0, description=test) + + test = ( + "We enter 3 consecutive Add Time tokens with the maximum amount of days and " + "ensure that they cumulate properly" + ) + for i in range(1, 3 + 1): + device_data, token_g7 = generate_from_device_data( + device_data, token_type=ADD_TIME, value_raw=995 + ) + test_how_many_days_validator( + device_simulator, + test_name("G7", i), + token_g7, + value_raw=995 * i, + device_data=device_data, + description=test, + ) + test = "" + + test = ( + "We enter 21 consecutive Set Time tokens for 1, 2, 3, … 21 days " + "each with a count 30 higher than the other. The validation of the " + "token should not take more than 5 seconds" + ) + for i in range(1, 21 + 1): + device_data, token_g8 = generate_from_device_data( + device_data, + token_type=SET_TIME, + value_days=i, + token_count=device_data["token_count"] + 29, + ) + test_how_many_days_validator( + device_simulator, + test_name("G8", i), + token_g8, + value_days=i, + device_data=device_data, + description=test, + ) + test = "" + + test = "We enter a PAYG Disable token into the device" + device_data, token_g9 = generate_from_device_data( + device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE + ) + test_how_many_days_validator( + device_simulator, "G9", token_g9, None, description=test + ) + + test = "We enter a Set Time token for 0 day, it should relock the device" + device_data, token_g10 = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=0 + ) + test_how_many_days_validator( + device_simulator, "G10", token_g10, 0, description=test + ) + + test = ( + "We enter a PAYG Disable token to relock the device, then enter a Add " + "Time token with 0 day, it should NOT relock the device (Optional)" + ) + device_data, token_g11a = generate_from_device_data( + device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE + ) + test_how_many_days_validator( + device_simulator, "G11A", token_g11a, None, description=test + ) + device_data, token_g11b = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=0 + ) + test_how_many_days_validator(device_simulator, "G11B", token_g11b, None) + + test = ( + "We deactivate the device with a Set Time of 0 days. We then wait " + "48 hours before entering a Add Time of 1 day and ensuring that the " + "days late are not considered in the activation time" + ) + device_data, token_g12a = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=0 + ) + test_how_many_days_validator( + device_simulator, "G12A", token_g12a, 0, description=test + ) + device_data, token_g12b = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=1 + ) + test_how_many_days_validator(device_simulator, "G12B", token_g12b, 1) return device_data def run_unordered_entry_tests(device_data, device_simulator): - - test = 'We generate 3 Add Time tokens, then enter the 3rd, then first, then second and ensure the days are added properly' - device_data, token_u1a = generate_from_device_data(device_data, token_type=SET_TIME, value_days=60) - test_how_many_days_validator(device_simulator, 'U1A', token_u1a, 60, description=test) - device_data, token_u1b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=10) - device_data, token_u1c = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=5) - device_data, token_u1d = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) - test_how_many_days_validator(device_simulator, 'U1B', token_u1d, 61) - test_how_many_days_validator(device_simulator, 'U1C', token_u1b, 71) - test_how_many_days_validator(device_simulator, 'U1D', token_u1c, 76) - - test = 'We generate an Add Time, a Set Time and another Add Time token. We enter the set time and ensure that the older Add Time does not work but the newer does' - device_data, token_u2a = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=5) - device_data, token_u2b = generate_from_device_data(device_data, token_type=SET_TIME, value_days=10) - device_data, token_u2c = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=3) - test_how_many_days_validator(device_simulator, 'U2A', token_u2b, 10, description=test) - test_how_many_days_validator(device_simulator, 'U2B', token_u2a, 10, accepted_but_used=True) - test_how_many_days_validator(device_simulator, 'U2C', token_u2c, 13) - - test = 'We generate an Add Time token and a Disable PAYG token, we enter the Disable PAYG token and then the Add Time token should be refused' - device_data, token_u3a = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) - device_data, token_u3b = generate_from_device_data(device_data, token_type=ADD_TIME, value_raw=DISABLE_VALUE) - test_how_many_days_validator(device_simulator, 'U3A', token_u3b, None, description=test) - test_how_many_days_validator(device_simulator, 'U3B', token_u3a, None, accepted_but_used=True) + test = ( + "We generate 3 Add Time tokens, then enter the 3rd, then first, then " + "second and ensure the days are added properly" + ) + device_data, token_u1a = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=60 + ) + test_how_many_days_validator( + device_simulator, "U1A", token_u1a, 60, description=test + ) + device_data, token_u1b = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=10 + ) + device_data, token_u1c = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=5 + ) + device_data, token_u1d = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=1 + ) + test_how_many_days_validator(device_simulator, "U1B", token_u1d, 61) + test_how_many_days_validator(device_simulator, "U1C", token_u1b, 71) + test_how_many_days_validator(device_simulator, "U1D", token_u1c, 76) + + test = ( + "We generate an Add Time, a Set Time and another Add Time token. We " + "enter the set time and ensure that the older Add Time does not work " + "but the newer does" + ) + device_data, token_u2a = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=5 + ) + device_data, token_u2b = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=10 + ) + device_data, token_u2c = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=3 + ) + test_how_many_days_validator( + device_simulator, "U2A", token_u2b, 10, description=test + ) + test_how_many_days_validator( + device_simulator, "U2B", token_u2a, 10, accepted_but_used=True + ) + test_how_many_days_validator(device_simulator, "U2C", token_u2c, 13) + + test = ( + "We generate an Add Time token and a Disable PAYG token, we enter the " + "Disable PAYG token and then the Add Time token should be refused" + ) + device_data, token_u3a = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=1 + ) + device_data, token_u3b = generate_from_device_data( + device_data, token_type=ADD_TIME, value_raw=DISABLE_VALUE + ) + test_how_many_days_validator( + device_simulator, "U3A", token_u3b, None, description=test + ) + test_how_many_days_validator( + device_simulator, "U3B", token_u3a, None, accepted_but_used=True + ) return device_data - - -if __name__ == '__main__': +if __name__ == "__main__": device_data = { - 'serial_number': 'XXX', - 'starting_code': 516959010, - 'key': 'bc41ec9530f6dac86b1a29ab82edc5fb', - 'restricted_digit_set': False, - 'time_divider': 1, - 'token_count': 1 + "serial_number": "XXX", + "starting_code": 516959010, + "key": "bc41ec9530f6dac86b1a29ab82edc5fb", + "restricted_digit_set": False, + "time_divider": 1, + "token_count": 1, } - device_simulator = DeviceSimulator(device_data['starting_code'], codecs.decode(device_data['key'], 'hex'), - restricted_digit_set=device_data['restricted_digit_set'], - waiting_period_enabled=False) + device_simulator = DeviceSimulator( + device_data["starting_code"], + codecs.decode(device_data["key"], "hex"), + restricted_digit_set=device_data["restricted_digit_set"], + waiting_period_enabled=False, + ) device_data = run_core_token_tests(device_data, device_simulator) device_data = run_unordered_entry_tests(device_data, device_simulator) diff --git a/test/helpers.py b/test/helpers.py index 1cd2029..d558ea2 100644 --- a/test/helpers.py +++ b/test/helpers.py @@ -1,69 +1,98 @@ import codecs -import openpaygo +import openpaygo from openpaygo.token_shared import TokenType - SET_TIME = TokenType.SET_TIME ADD_TIME = TokenType.ADD_TIME DISABLE_VALUE = 998 -START_RED = '\033[91m' -END_RED = '\033[0m' +START_RED = "\033[91m" +END_RED = "\033[0m" -def generate_from_device_data(device_data, token_type, value_raw=None, value_days=None, token_count=None): +def generate_from_device_data( + device_data, token_type, value_raw=None, value_days=None, token_count=None +): assert (value_days is not None) or (value_raw is not None) if value_raw is None: - value_raw = value_days*device_data['time_divider'] - device_data['token_count'], token = openpaygo.token_encode.OpenPAYGOTokenEncoder.generate_standard_token( - starting_code=device_data['starting_code'], - key=codecs.decode(device_data['key'], 'hex'), - value=value_raw, - count=token_count or device_data['token_count'], - restricted_digit_set=device_data['restricted_digit_set'], - mode=token_type + value_raw = value_days * device_data["time_divider"] + device_data["token_count"], token = ( + openpaygo.token_encode.OpenPAYGOTokenEncoder.generate_standard_token( + starting_code=device_data["starting_code"], + key=codecs.decode(device_data["key"], "hex"), + value=value_raw, + count=token_count or device_data["token_count"], + restricted_digit_set=device_data["restricted_digit_set"], + mode=token_type, + ) ) - token = str(token).rjust(9, '0') - token = ' '.join([token[i:i + 3] for i in range(0, len(token), 3)]) + token = str(token).rjust(9, "0") + token = " ".join([token[i : i + 3] for i in range(0, len(token), 3)]) return device_data, token -def test_how_many_days(test_name, token, value_days=None, value_raw=None, device_data=None, description=''): +def test_how_many_days( + test_name, token, value_days=None, value_raw=None, device_data=None, description="" +): if value_days is None: if value_raw is not None: - value_days = value_raw/device_data['time_divider'] + value_days = value_raw / device_data["time_divider"] else: - value_days = 'infinite' - print(test_name+','+token+','+str(value_days)+' days active'+',"'+description+'"') + value_days = "infinite" + print( + test_name + + "," + + token + + "," + + str(value_days) + + " days active" + + ',"' + + description + + '"' + ) -def test_accepted(test_name, token, expected, description=''): - expected_string = 'Token Accepted' if expected else 'Token Refused' - print(test_name+','+token+','+expected_string+',"'+description+'"') +def test_accepted(test_name, token, expected, description=""): + expected_string = "Token Accepted" if expected else "Token Refused" + print(test_name + "," + token + "," + expected_string + ',"' + description + '"') -def test_how_many_days_validator(device_simulator, test_name, token, value_days=None, value_raw=None, device_data=None, description='', accepted_but_used=False): +def test_how_many_days_validator( + device_simulator, + test_name, + token, + value_days=None, + value_raw=None, + device_data=None, + description="", + accepted_but_used=False, +): if value_days is None: if value_raw is not None: - value_days = value_raw/device_data['time_divider'] + value_days = value_raw / device_data["time_divider"] else: - value_days = 'infinite' - result = device_simulator.enter_token(token.replace(' ', ''), show_result=False) + value_days = "infinite" + result = device_simulator.enter_token(token.replace(" ", ""), show_result=False) if result == 1 or (accepted_but_used and result == -2): if device_simulator.get_days_remaining() == value_days: - print(test_name+': Passed') + print(test_name + ": Passed") return - print(START_RED+test_name+': Failed'+END_RED) + print(START_RED + test_name + ": Failed" + END_RED) -def test_accepted_validator(device_simulator, test_name, token, expected, description=''): - if device_simulator.enter_token(token.replace(' ', ''), show_result=False) == expected: - print(test_name+': Passed') +def test_accepted_validator( + device_simulator, test_name, token, expected, description="" +): + if ( + device_simulator.enter_token(token.replace(" ", ""), show_result=False) + == expected + ): + print(test_name + ": Passed") return else: - print(START_RED+test_name+': Failed'+END_RED) + print(START_RED + test_name + ": Failed" + END_RED) def test_name(test_base_name, test_number): - return test_base_name+chr(test_number+64) + return test_base_name + chr(test_number + 64) diff --git a/test/simple_scenario_test.py b/test/simple_scenario_test.py index 59ab763..1b73341 100644 --- a/test/simple_scenario_test.py +++ b/test/simple_scenario_test.py @@ -1,120 +1,160 @@ from datetime import datetime, timedelta + from openpaygo.simulators import DeviceSimulator, SingleDeviceServerSimulator from openpaygo.token_shared import TokenType def assert_time_equals(time1, time2): - assert time1.replace(second=0, microsecond=0) == time2.replace(second=0, microsecond=0) + assert time1.replace(second=0, microsecond=0) == time2.replace( + second=0, microsecond=0 + ) -if __name__ == '__main__': +if __name__ == "__main__": # ------ IMPORTANT WARNING -------- # DO NOT USE THIS KEY IN PRODUCTION # IT IS JUST AN EXAMPLE # --------------------------------- - device_key = b'\xa2\x9a\xb8.\xdc_\xbb\xc4\x1e\xc9S\x0fm\xac\x86\xb1' - device_starting_code = 123456789 # Generated by fair dice roll + device_key = b"\xa2\x9a\xb8.\xdc_\xbb\xc4\x1e\xc9S\x0fm\xac\x86\xb1" + device_starting_code = 123456789 # Generated by fair dice roll restricted_digit_set = False - print('Device: We initiate the device simulator with our device') - device_simulator = DeviceSimulator(device_starting_code, device_key, - restricted_digit_set=restricted_digit_set, - waiting_period_enabled=False) - print('Server: We initiate the server simulator with our device') - server_simulator = SingleDeviceServerSimulator(device_starting_code, device_key, - restricted_digit_set=restricted_digit_set) - - print('\n') - print('Device: We try entering an invalid token into the device: 123456789') - device_simulator.enter_token('123456789') - print('Device: We check the device status (should be still inactive)') + print("Device: We initiate the device simulator with our device") + device_simulator = DeviceSimulator( + device_starting_code, + device_key, + restricted_digit_set=restricted_digit_set, + waiting_period_enabled=False, + ) + print("Server: We initiate the server simulator with our device") + server_simulator = SingleDeviceServerSimulator( + device_starting_code, device_key, restricted_digit_set=restricted_digit_set + ) + + print("\n") + print("Device: We try entering an invalid token into the device: 123456789") + device_simulator.enter_token("123456789") + print("Device: We check the device status (should be still inactive)") device_simulator.print_status() - print('\n') - print('Server: We add 1 days of activation for the device') - this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) - print('Token: '+this_token) - print('Device: We enter the generated token into the device') + print("\n") + print("Server: We add 1 days of activation for the device") + this_token = server_simulator.generate_token_from_date( + datetime.now() + timedelta(days=1) + ) + print("Token: " + this_token) + print("Device: We enter the generated token into the device") device_simulator.enter_token(this_token) - print('Device: We check the device status (should be active with 1 day)') + print("Device: We check the device status (should be active with 1 day)") device_simulator.print_status() assert device_simulator.count == server_simulator.count - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1)) + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=1) + ) - print('\n') - print('Device: We enter the token a second time to make sure it doesnt add the days again') + print("\n") + print( + "Device: We enter the token a second time to make sure it doesnt add the " + "days again" + ) device_simulator.enter_token(this_token) - print('Device: We check the device status (should be active with 1 day)') + print("Device: We check the device status (should be active with 1 day)") device_simulator.print_status() assert device_simulator.count == server_simulator.count - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1)) + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=1) + ) - print('\n') - print('Server: We set it to expire in 30 days') - this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=30)) - print('Token: ' + this_token) - print('Device: We enter the generated token into the device') + print("\n") + print("Server: We set it to expire in 30 days") + this_token = server_simulator.generate_token_from_date( + datetime.now() + timedelta(days=30) + ) + print("Token: " + this_token) + print("Device: We enter the generated token into the device") device_simulator.enter_token(this_token) - print('Device: We check the device status (should be active with 30 days)') + print("Device: We check the device status (should be active with 30 days)") device_simulator.print_status() assert device_simulator.count == server_simulator.count - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=30)) + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=30) + ) - print('\n') - print('Server: We set it to expire in 7 days (removing 23 days)') - this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=7)) - print('Token: ' + this_token) - print('Device: We enter the generated token into the device') + print("\n") + print("Server: We set it to expire in 7 days (removing 23 days)") + this_token = server_simulator.generate_token_from_date( + datetime.now() + timedelta(days=7) + ) + print("Token: " + this_token) + print("Device: We enter the generated token into the device") device_simulator.enter_token(this_token) - print('Device: We check the device status (should be active with 7 days)') + print("Device: We check the device status (should be active with 7 days)") device_simulator.print_status() assert device_simulator.count == server_simulator.count - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=7)) - - print('\n') - print('Server: We generate a token for putting the device in PAYG-OFF mode') - this_PAYG_OFF_code = server_simulator.generate_payg_disable_token() - this_PAYG_OFF_code = str(this_PAYG_OFF_code) - print('Token: ' + this_PAYG_OFF_code) - print('Device: We enter the generated token into the device') - device_simulator.enter_token(this_PAYG_OFF_code) - print('Device: We check the device status (should be active forver)') + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=7) + ) + + print("\n") + print("Server: We generate a token for putting the device in PAYG-OFF mode") + this_payg_off_code = server_simulator.generate_payg_disable_token() + this_payg_off_code = str(this_payg_off_code) + print("Token: " + this_payg_off_code) + print("Device: We enter the generated token into the device") + device_simulator.enter_token(this_payg_off_code) + print("Device: We check the device status (should be active forver)") device_simulator.print_status() assert device_simulator.count == server_simulator.count assert device_simulator.payg_enabled is False - print('\n') - print('Server: We generate a token for putting the device back PAYG-ON mode with 0 days') - this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=0)) + print("\n") + print( + "Server: We generate a token for putting the device back PAYG-ON mode " + "with 0 days" + ) + this_token = server_simulator.generate_token_from_date( + datetime.now() + timedelta(days=0) + ) this_token = str(this_token) - print('Token: ' + this_token) - print('Device: We enter the generated token into the device') + print("Token: " + this_token) + print("Device: We enter the generated token into the device") device_simulator.enter_token(this_token) - print('Device: We check the device status (should not be active)') + print("Device: We check the device status (should not be active)") device_simulator.print_status() assert device_simulator.count == server_simulator.count assert device_simulator.payg_enabled is True - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=0)) + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=0) + ) - print('\n') - print('Server: We generate a bunch of 1 day tokens, but only enter the latest one') + print("\n") + print( + "Server: We generate a bunch of 1 day tokens, but only enter the latest " "one" + ) server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) - this_token = server_simulator.generate_token_from_date(datetime.now() + timedelta(days=1)) + this_token = server_simulator.generate_token_from_date( + datetime.now() + timedelta(days=1) + ) this_token = str(this_token) - print('Token: ' + this_token) - print('Device: We enter the latest generated token into the device') + print("Token: " + this_token) + print("Device: We enter the latest generated token into the device") device_simulator.enter_token(this_token) - print('Device: We check the device status (should be active with 1 day and the count synchronised with the server)') + print( + "Device: We check the device status (should be active with 1 day and " + "the count synchronised with the server)" + ) device_simulator.print_status() assert device_simulator.count == server_simulator.count - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1)) + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=1) + ) - print('\n') - print('Server: We add generate 9 tokens each add-time of 1 day') + print("\n") + print("Server: We add generate 9 tokens each add-time of 1 day") token_1 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) token_2 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) token_3 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) @@ -124,59 +164,96 @@ def assert_time_equals(time1, time2): token_7 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) token_8 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) token_9 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) - print('Tokens: ', token_1, token_2, token_3, token_4, token_5, token_6, token_7, token_8, token_9) - print('Device: We enter the 9th token into the device') + print( + "Tokens: ", + token_1, + token_2, + token_3, + token_4, + token_5, + token_6, + token_7, + token_8, + token_9, + ) + print("Device: We enter the 9th token into the device") device_simulator.enter_token(token_9) - print('Device: We check the device status (should be active with +1 day (2 days total)') + print( + "Device: We check the device status (should be active with +1 day (2 " + "days total)" + ) device_simulator.print_status() assert device_simulator.count == server_simulator.count - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=2)) - print('Device: We enter the 1st token into the device') + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=2) + ) + print("Device: We enter the 1st token into the device") device_simulator.enter_token(token_1) - print('Device: We check the device status , it should not have changed, because its more than 5 add times before') + print( + "Device: We check the device status , it should not have changed, " + "because its more than 5 add times before" + ) device_simulator.print_status() - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=2)) - print('Device: We enter the tokens 5, 4, 3 and 2 into the device') + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=2) + ) + print("Device: We enter the tokens 5, 4, 3 and 2 into the device") device_simulator.enter_token(token_5) device_simulator.enter_token(token_4) device_simulator.enter_token(token_3) device_simulator.enter_token(token_2) - print('Device: We check the device status , it should have +4 days (6 days total)') + print( + "Device: We check the device status , it should have +4 days (6 days " "total)" + ) device_simulator.print_status() - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=6)) + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=6) + ) - print('\n') - print('Server: We add generate 2 tokens, first add-time and then set-time') + print("\n") + print("Server: We add generate 2 tokens, first add-time and then set-time") token_1 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) token_2 = server_simulator._generate_token_from_value(0, TokenType.SET_TIME) - print('Tokens: ', token_1, token_2) - print('Device: We enter the 2nd token') + print("Tokens: ", token_1, token_2) + print("Device: We enter the 2nd token") device_simulator.enter_token(token_2) - print('Device: We check the device status should be active in 0 days.') + print("Device: We check the device status should be active in 0 days.") device_simulator.print_status() assert device_simulator.count == server_simulator.count - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=0)) - print('Device: We enter the 1st token into the device') + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=0) + ) + print("Device: We enter the 1st token into the device") device_simulator.enter_token(token_1) - print('Device: We check the device status , it should not have changed, ' - 'because you cannot use an add-time token older than a set-time') + print( + "Device: We check the device status , it should not have changed, " + "because you cannot use an add-time token older than a set-time" + ) device_simulator.print_status() - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=0)) + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=0) + ) - print('\n') - print('Server: We add generate 2 tokens, first add-time and then set-time') + print("\n") + print("Server: We add generate 2 tokens, first add-time and then set-time") token_1 = server_simulator._generate_token_from_value(1, TokenType.SET_TIME) token_2 = server_simulator._generate_token_from_value(1, TokenType.ADD_TIME) - print('Tokens: ', token_1, token_2) - print('Device: We enter the 2nd token') + print("Tokens: ", token_1, token_2) + print("Device: We enter the 2nd token") device_simulator.enter_token(token_2) - print('Device: We check the device status should be active in 0 days.') + print("Device: We check the device status should be active in 0 days.") device_simulator.print_status() assert device_simulator.count == server_simulator.count - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1)) - print('Device: We enter the 1st token into the device') + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=1) + ) + print("Device: We enter the 1st token into the device") device_simulator.enter_token(token_1) - print('Device: We check the device status , it should not have changed, ' - 'because you cannot use an older set-time token') + print( + "Device: We check the device status , it should not have changed, " + "because you cannot use an older set-time token" + ) device_simulator.print_status() - assert_time_equals(device_simulator.expiration_date, datetime.now() + timedelta(days=1)) + assert_time_equals( + device_simulator.expiration_date, datetime.now() + timedelta(days=1) + ) diff --git a/test/spreadsheet_generator_test.py b/test/spreadsheet_generator_test.py index be23d08..9fc45e8 100755 --- a/test/spreadsheet_generator_test.py +++ b/test/spreadsheet_generator_test.py @@ -1,109 +1,207 @@ -from test.helpers import ADD_TIME, SET_TIME, DISABLE_VALUE, generate_from_device_data, test_accepted, test_how_many_days, test_name __test__ = False +from test.helpers import ( + ADD_TIME, + DISABLE_VALUE, + SET_TIME, + generate_from_device_data, + test_accepted, + test_how_many_days, + test_name, +) -def run_core_token_tests(device_data): - test = 'We enter an invalid token' - token_g1 = '123 456 789' - test_accepted('G1', token_g1, False, description=test) - - test = 'We enter a valid token for setting one day' - device_data, token_g2 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=1) - test_how_many_days('G2', token_g2, 1, description=test) - - test = 'We enter a valid token for adding one day' - device_data, token_g3 = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) - test_how_many_days('G3', token_g3, 2, description=test) - - test = 'We enter the same Add Time token for 1 day, the days should not be added and the device should signal that the token was already used' - test_how_many_days('G4A', token_g3, 2, description=test) - test = 'We enter the older Set Time token for 1 day, the days should not change and the device should signal that the token was already used' - test_how_many_days('G4B', token_g2, 2, description=test) - - test = 'We enter a valid token for setting 30 days and ensures it sets and does not add to the existing' - device_data, token_g5 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=30) - test_how_many_days('G5', token_g5, 30, description=test) - - test = 'We enter a valid token for setting 0 days and ensures the device is inactive with the outputs disabled immediately' - device_data, token_g6 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) - test_how_many_days('G6', token_g6, 0, description=test) - - test = 'We enter 3 consecutive Add Time tokens with the maximum amount of days and ensure that they cumulate properly' - for i in range(1,3+1): - device_data, token_g7 = generate_from_device_data(device_data, token_type=ADD_TIME, value_raw=995) - test_how_many_days(test_name('G7', i), token_g7, value_raw=995*i, device_data=device_data, description=test) - test = '' - - test = 'We enter 21 consecutive Set Time tokens for 1, 2, 3, … 21 days each with a count 30 higher than the other. The validation of the token should not take more than 5 seconds' - for i in range(1,21+1): - device_data, token_g8 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=i, token_count=device_data['token_count']+29) - test_how_many_days(test_name('G8', i), token_g8, value_days=i, device_data=device_data, description=test) - test = '' - - test = 'We enter a PAYG Disable token into the device' - device_data, token_g9 = generate_from_device_data(device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE) - test_how_many_days('G9', token_g9, None, description=test) - - test = 'We enter a Set Time token for 0 day, it should relock the device' - device_data, token_g10 = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) - test_how_many_days('G10', token_g10, 0, description=test) - - test = 'We enter a PAYG Disable token to relock the device, then enter a Add Time token with 0 day, it should NOT relock the device (Optional)' - device_data, token_g11a = generate_from_device_data(device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE) - test_how_many_days('G11A', token_g11a, None, description=test) - device_data, token_g11b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=0) - test_how_many_days('G11B', token_g11b, None) - - test = 'We deactivate the device with a Set Time of 0 days. We then wait 48 hours before entering a Add Time of 1 day and ensuring that the days late are not considered in the activation time' - device_data, token_g12a = generate_from_device_data(device_data, token_type=SET_TIME, value_days=0) - test_how_many_days('G12A', token_g12a, 0, description=test) - device_data, token_g12b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) - test_how_many_days('G12B', token_g12b, 1) +def run_core_token_tests(device_data): + test = "We enter an invalid token" + token_g1 = "123 456 789" + test_accepted("G1", token_g1, False, description=test) + + test = "We enter a valid token for setting one day" + device_data, token_g2 = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=1 + ) + test_how_many_days("G2", token_g2, 1, description=test) + + test = "We enter a valid token for adding one day" + device_data, token_g3 = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=1 + ) + test_how_many_days("G3", token_g3, 2, description=test) + + test = ( + "We enter the same Add Time token for 1 day, the days should not be " + "added and the device should signal that the token was already used" + ) + test_how_many_days("G4A", token_g3, 2, description=test) + test = ( + "We enter the older Set Time token for 1 day, the days should not " + "change and the device should signal that the token was already used" + ) + test_how_many_days("G4B", token_g2, 2, description=test) + + test = ( + "We enter a valid token for setting 30 days and ensures it sets and does " + "not add to the existing" + ) + device_data, token_g5 = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=30 + ) + test_how_many_days("G5", token_g5, 30, description=test) + + test = ( + "We enter a valid token for setting 0 days and ensures the device is " + "inactive with the outputs disabled immediately" + ) + device_data, token_g6 = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=0 + ) + test_how_many_days("G6", token_g6, 0, description=test) + + test = ( + "We enter 3 consecutive Add Time tokens with the maximum amount of days " + "and ensure that they cumulate properly" + ) + for i in range(1, 3 + 1): + device_data, token_g7 = generate_from_device_data( + device_data, token_type=ADD_TIME, value_raw=995 + ) + test_how_many_days( + test_name("G7", i), + token_g7, + value_raw=995 * i, + device_data=device_data, + description=test, + ) + test = "" + + test = ( + "We enter 21 consecutive Set Time tokens for 1, 2, 3, … 21 days each " + "with a count 30 higher than the other. The validation of the token " + "should not take more than 5 seconds" + ) + for i in range(1, 21 + 1): + device_data, token_g8 = generate_from_device_data( + device_data, + token_type=SET_TIME, + value_days=i, + token_count=device_data["token_count"] + 29, + ) + test_how_many_days( + test_name("G8", i), + token_g8, + value_days=i, + device_data=device_data, + description=test, + ) + test = "" + + test = "We enter a PAYG Disable token into the device" + device_data, token_g9 = generate_from_device_data( + device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE + ) + test_how_many_days("G9", token_g9, None, description=test) + + test = "We enter a Set Time token for 0 day, it should relock the device" + device_data, token_g10 = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=0 + ) + test_how_many_days("G10", token_g10, 0, description=test) + + test = ( + "We enter a PAYG Disable token to relock the device, then enter a Add " + "Time token with 0 day, it should NOT relock the device (Optional)" + ) + device_data, token_g11a = generate_from_device_data( + device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE + ) + test_how_many_days("G11A", token_g11a, None, description=test) + device_data, token_g11b = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=0 + ) + test_how_many_days("G11B", token_g11b, None) + + test = ( + "We deactivate the device with a Set Time of 0 days. We then wait 48 " + "hours before entering a Add Time of 1 day and ensuring that the days " + "late are not considered in the activation time" + ) + device_data, token_g12a = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=0 + ) + test_how_many_days("G12A", token_g12a, 0, description=test) + device_data, token_g12b = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=1 + ) + test_how_many_days("G12B", token_g12b, 1) return device_data def run_unordered_entry_tests(device_data): - - test = 'We generate 3 Add Time tokens, then enter the 3rd, then first, then second and ensure the days are added properly' - device_data, token_u1a = generate_from_device_data(device_data, token_type=SET_TIME, value_days=60) - test_how_many_days('U1A', token_u1a, 60, description=test) - device_data, token_u1b = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=10) - device_data, token_u1c = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=5) - device_data, token_u1d = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) - test_how_many_days('U1B', token_u1d, 61) - test_how_many_days('U1C', token_u1b, 71) - test_how_many_days('U1D', token_u1c, 76) - - test = 'We generate an Add Time, a Set Time and another Add Time token. We enter the set time and ensure that the older Add Time does not work but the newer does' - device_data, token_u2a = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=5) - device_data, token_u2b = generate_from_device_data(device_data, token_type=SET_TIME, value_days=10) - device_data, token_u2c = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=3) - test_how_many_days('U2A', token_u2b, 10, description=test) - test_accepted('U2B', token_u2a, False) - test_how_many_days('U2C', token_u2c, 13) - - test = 'We generate an Add Time token and a Disable PAYG token, we enter the Disable PAYG token and then the Add Time token should be refused' - device_data, token_u3a = generate_from_device_data(device_data, token_type=ADD_TIME, value_days=1) - device_data, token_u3b = generate_from_device_data(device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE) - test_how_many_days('U3A', token_u3b, None, description=test) - test_accepted('U3B', token_u3a, False) + test = ( + "We generate 3 Add Time tokens, then enter the 3rd, then first, then " + "second and ensure the days are added properly" + ) + device_data, token_u1a = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=60 + ) + test_how_many_days("U1A", token_u1a, 60, description=test) + device_data, token_u1b = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=10 + ) + device_data, token_u1c = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=5 + ) + device_data, token_u1d = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=1 + ) + test_how_many_days("U1B", token_u1d, 61) + test_how_many_days("U1C", token_u1b, 71) + test_how_many_days("U1D", token_u1c, 76) + + test = ( + "We generate an Add Time, a Set Time and another Add Time token. We " + "enter the set time and ensure that the older Add Time does not work " + "but the newer does" + ) + device_data, token_u2a = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=5 + ) + device_data, token_u2b = generate_from_device_data( + device_data, token_type=SET_TIME, value_days=10 + ) + device_data, token_u2c = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=3 + ) + test_how_many_days("U2A", token_u2b, 10, description=test) + test_accepted("U2B", token_u2a, False) + test_how_many_days("U2C", token_u2c, 13) + + test = ( + "We generate an Add Time token and a Disable PAYG token, we enter the " + "Disable PAYG token and then the Add Time token should be refused" + ) + device_data, token_u3a = generate_from_device_data( + device_data, token_type=ADD_TIME, value_days=1 + ) + device_data, token_u3b = generate_from_device_data( + device_data, token_type=SET_TIME, value_raw=DISABLE_VALUE + ) + test_how_many_days("U3A", token_u3b, None, description=test) + test_accepted("U3B", token_u3a, False) return device_data - - -if __name__ == '__main__': +if __name__ == "__main__": device_data = { - 'serial_number': 'changeme', - 'starting_code': 123456789, - 'key': 'a29ab82edc5fbbc41ec9530f6dac86b1', - 'restricted_digit_set': False, - 'time_divider': 1, - 'token_count': 1 + "serial_number": "changeme", + "starting_code": 123456789, + "key": "a29ab82edc5fbbc41ec9530f6dac86b1", + "restricted_digit_set": False, + "time_divider": 1, + "token_count": 1, } - print('Test Name,Token Used,Expected Result,Test Details,Result') + print("Test Name,Token Used,Expected Result,Test Details,Result") device_data = run_core_token_tests(device_data) device_data = run_unordered_entry_tests(device_data) From 1400dd418b86b649b0e3895873bdf36680af05c7 Mon Sep 17 00:00:00 2001 From: CoderOMaster Date: Tue, 23 Dec 2025 23:33:01 +0530 Subject: [PATCH 7/8] changes --- openpaygo/metrics_shared.py | 3 +- openpaygo/simulators/__init__.py | 6 +- openpaygo/simulators/device_simulator.py | 70 +++++++++++++++++------- openpaygo/simulators/server_simulator.py | 21 +++++-- openpaygo/token_shared.py | 4 +- 5 files changed, 75 insertions(+), 29 deletions(-) diff --git a/openpaygo/metrics_shared.py b/openpaygo/metrics_shared.py index cae1579..938857d 100644 --- a/openpaygo/metrics_shared.py +++ b/openpaygo/metrics_shared.py @@ -150,7 +150,8 @@ def load_secret_key_from_hex(cls, secret_key): secret_key_bytes = bytes(secret_key) if len(secret_key_bytes) != 16: raise ValueError( - "The secret key provided is not correctly formatted, it should be 16 bytes. " + "The secret key provided is not correctly formatted, it should be 16 " + "bytes. " ) return secret_key_bytes diff --git a/openpaygo/simulators/__init__.py b/openpaygo/simulators/__init__.py index 618cc96..c01c5aa 100644 --- a/openpaygo/simulators/__init__.py +++ b/openpaygo/simulators/__init__.py @@ -1,2 +1,4 @@ -from .device_simulator import DeviceSimulator -from .server_simulator import SingleDeviceServerSimulator \ No newline at end of file +from .device_simulator import DeviceSimulator as DeviceSimulator +from .server_simulator import SingleDeviceServerSimulator as SingleDeviceServerSimulator + +__all__ = ["DeviceSimulator", "SingleDeviceServerSimulator"] \ No newline at end of file diff --git a/openpaygo/simulators/device_simulator.py b/openpaygo/simulators/device_simulator.py index 5468170..8737f66 100644 --- a/openpaygo/simulators/device_simulator.py +++ b/openpaygo/simulators/device_simulator.py @@ -1,16 +1,27 @@ from datetime import datetime, timedelta -from openpaygo.token_shared import OpenPAYGOTokenShared + from openpaygo.token_decode import OpenPAYGOTokenDecoder, TokenType +from openpaygo.token_shared import OpenPAYGOTokenShared class DeviceSimulator(object): - def __init__(self, starting_code, key, starting_count=1, restricted_digit_set=False, waiting_period_enabled=True, time_divider=1): + def __init__( + self, + starting_code, + key, + starting_count=1, + restricted_digit_set=False, + waiting_period_enabled=True, + time_divider=1, + ): self.starting_code = starting_code self.key = key self.time_divider = time_divider self.restricted_digit_set = restricted_digit_set - self.waiting_period_enabled = waiting_period_enabled # Should always be true except for testing + self.waiting_period_enabled = ( + waiting_period_enabled # Should always be true except for testing + ) self.payg_enabled = True self.count = starting_count @@ -48,18 +59,23 @@ def get_days_remaining(self): return 'infinite' def _update_device_status_from_token(self, token, show_result=True): - if self.token_entry_blocked_until > datetime.now() and self.waiting_period_enabled: + if ( + self.token_entry_blocked_until > datetime.now() + and self.waiting_period_enabled + ): if show_result: print('TOKEN_ENTRY_BLOCKED') return False - token_value, token_type, token_count, updated_counts = OpenPAYGOTokenDecoder.get_activation_value_count_and_type_from_token( - token=token, - starting_code=self.starting_code, - key=self.key, - last_count=self.count, - restricted_digit_set=self.restricted_digit_set, - used_counts=self.used_counts + token_value, token_type, token_count, updated_counts = ( + OpenPAYGOTokenDecoder.get_activation_value_count_and_type_from_token( + token=token, + starting_code=self.starting_code, + key=self.key, + last_count=self.count, + restricted_digit_set=self.restricted_digit_set, + used_counts=self.used_counts, + ) ) if token_value is None: if token_type == TokenType.ALREADY_USED: @@ -69,7 +85,9 @@ def _update_device_status_from_token(self, token, show_result=True): if show_result: print('TOKEN_INVALID') self.invalid_token_count += 1 - self.token_entry_blocked_until = datetime.now() + timedelta(minutes=2**self.invalid_token_count) + self.token_entry_blocked_until = datetime.now() + timedelta( + minutes=2**self.invalid_token_count + ) return -1 elif token_value == -2: if show_result: @@ -78,7 +96,10 @@ def _update_device_status_from_token(self, token, show_result=True): else: if show_result: print('TOKEN_VALID', ' | Value:', token_value) - if token_count > self.count or token_value == OpenPAYGOTokenShared.COUNTER_SYNC_VALUE: + if ( + token_count > self.count + or token_value == OpenPAYGOTokenShared.COUNTER_SYNC_VALUE + ): self.count = token_count self.used_counts = updated_counts self.invalid_token_count = 0 @@ -86,22 +107,29 @@ def _update_device_status_from_token(self, token, show_result=True): return 1 def _update_device_status_from_extended_token(self, token, show_result=True): - if self.token_entry_blocked_until > datetime.now() and self.waiting_period_enabled: + if ( + self.token_entry_blocked_until > datetime.now() + and self.waiting_period_enabled + ): if show_result: print('TOKEN_ENTRY_BLOCKED') - token_value, token_count = OpenPAYGOTokenDecoder.get_activation_value_count_from_extended_token( - token=token, - starting_code=self.starting_code, - key=self.key, - last_count=self.count, - restricted_digit_set=self.restricted_digit_set + token_value, token_count = ( + OpenPAYGOTokenDecoder.get_activation_value_count_from_extended_token( + token=token, + starting_code=self.starting_code, + key=self.key, + last_count=self.count, + restricted_digit_set=self.restricted_digit_set, + ) ) if token_value is None: if show_result: print('TOKEN_INVALID') self.invalid_token_count += 1 - self.token_entry_blocked_until = datetime.now() + timedelta(minutes=2**self.invalid_token_count) + self.token_entry_blocked_until = datetime.now() + timedelta( + minutes=2**self.invalid_token_count + ) else: if show_result: print('Special token entered, value: '+str(token_value)) diff --git a/openpaygo/simulators/server_simulator.py b/openpaygo/simulators/server_simulator.py index c1a7888..aeb2f9d 100644 --- a/openpaygo/simulators/server_simulator.py +++ b/openpaygo/simulators/server_simulator.py @@ -1,11 +1,19 @@ from datetime import datetime + from openpaygo.token_encode import OpenPAYGOTokenEncoder from openpaygo.token_shared import OpenPAYGOTokenShared, TokenType class SingleDeviceServerSimulator(object): - def __init__(self, starting_code, key, starting_count=1, restricted_digit_set=False, time_divider=1): + def __init__( + self, + starting_code, + key, + starting_count=1, + restricted_digit_set=False, + time_divider=1, + ): self.starting_code = starting_code self.key = key self.count = starting_count @@ -49,12 +57,16 @@ def generate_token_from_date(self, new_expiration_date, force=False): if new_expiration_date > furthest_expiration_date: # If the date is strictly above the furthest date activated, use ADD - value = self._get_value_to_activate(new_expiration_date, self.expiration_date, force) + value = self._get_value_to_activate( + new_expiration_date, self.expiration_date, force + ) self.expiration_date = new_expiration_date return self._generate_token_from_value(value, mode=TokenType.ADD_TIME) else: # If the date is below or equal to the furthest date activated, use SET - value = self._get_value_to_activate(new_expiration_date, datetime.now(), force) + value = self._get_value_to_activate( + new_expiration_date, datetime.now(), force + ) self.expiration_date = new_expiration_date return self._generate_token_from_value(value, mode=TokenType.SET_TIME) @@ -82,7 +94,8 @@ def _get_value_to_activate(self, new_time, reference_time, force_maximum=False): if not force_maximum: raise Exception('TOO_MANY_DAYS_TO_ACTIVATE') else: - return OpenPAYGOTokenShared.MAX_ACTIVATION_VALUE # Will need to be activated again after those days + # Will need to be activated again after those days + return OpenPAYGOTokenShared.MAX_ACTIVATION_VALUE return value @staticmethod diff --git a/openpaygo/token_shared.py b/openpaygo/token_shared.py index f6f0bca..d7df940 100644 --- a/openpaygo/token_shared.py +++ b/openpaygo/token_shared.py @@ -1,5 +1,6 @@ import codecs import struct + import siphash @@ -64,7 +65,8 @@ def load_secret_key_from_hex(cls, secret_key): secret_key_bytes = bytes(secret_key) if len(secret_key_bytes) != 16: raise ValueError( - "The secret key provided is not correctly formatted, it should be 16 bytes. " + "The secret key provided is not correctly formatted, it should be 16 " + "bytes. " ) return secret_key_bytes From 86058684ad5179a370217d1a8aca674a28f470c1 Mon Sep 17 00:00:00 2001 From: CoderOMaster Date: Wed, 24 Dec 2025 18:48:47 +0530 Subject: [PATCH 8/8] readme_fix --- test/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/README.md b/test/README.md index c818da0..7005540 100644 --- a/test/README.md +++ b/test/README.md @@ -9,3 +9,9 @@ $ source env/bin/activate $ python -m pip install -e .. $ python simple_scenario_test.py ``` + +You can do it for full test procedure as well: + +``` +$ python full_test_procedure.py +```