{"id":22399938,"url":"https://github.com/laugharne/testvote","last_synced_at":"2025-07-08T09:38:54.251Z","repository":{"id":172225958,"uuid":"648995086","full_name":"Laugharne/TestVote","owner":"Laugharne","description":"Projet #2 - Système de vote (test unit)","archived":false,"fork":false,"pushed_at":"2023-06-12T15:05:22.000Z","size":313,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-01T05:41:50.076Z","etag":null,"topics":["alyra","solidity","solidity-language","test","test-unit","test-unitaire","test-units","truffle","truffle-framework","truffle-testing","units"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Laugharne.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-06-03T12:54:12.000Z","updated_at":"2023-12-14T15:08:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"779370ce-8d1b-4f18-9165-8350c9beb24e","html_url":"https://github.com/Laugharne/TestVote","commit_stats":null,"previous_names":["laugharne/testvote"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Laugharne%2FTestVote","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Laugharne%2FTestVote/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Laugharne%2FTestVote/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Laugharne%2FTestVote/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Laugharne","download_url":"https://codeload.github.com/Laugharne/TestVote/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245755683,"owners_count":20667027,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alyra","solidity","solidity-language","test","test-unit","test-unitaire","test-units","truffle","truffle-framework","truffle-testing","units"],"created_at":"2024-12-05T08:10:36.202Z","updated_at":"2025-03-27T00:17:07.946Z","avatar_url":"https://github.com/Laugharne.png","language":"JavaScript","readme":"\n# Projet #2 - Système de vote (test unit)\n\n## Sommaire\n\n\n- [Projet #2 - Système de vote test unit](#projet-2---syst%C3%A8me-de-vote-test-unit)\n\t- [Sommaire](#sommaire)\n\t\t- [Enoncé](#enonc%C3%A9)\n\t\t\t- [Lien énoncé](#lien-%C3%A9nonc%C3%A9)\n\t\t\t- [Smart contract proposé](#smart-contract-propos%C3%A9)\n\t- [Explications](#explications)\n\t\t- [Constantes](#constantes)\n\t\t\t- [Extrait #1](#extrait-1)\n\t\t\t- [Extrait #2](#extrait-2)\n\t\t\t- [Extrait #3](#extrait-3)\n\t\t- [Les différents vecteurs d'états \u0026 variables](#les-diff%C3%A9rents-vecteurs-d%C3%A9tats--variables)\n\t\t\t- [Extrait](#extrait)\n\t\t- [Factorisation](#factorisation)\n\t\t\t- [Extraits](#extraits)\n\t\t- [Déploiement](#d%C3%A9ploiement)\n\t\t\t- [Extrait](#extrait)\n\t\t- [Évolution du processus de vote](#%C3%A9volution-du-processus-de-vote)\n\t\t\t- [Les étapes du déroulement du vote sont les suivantes](#les-%C3%A9tapes-du-d%C3%A9roulement-du-vote-sont-les-suivantes)\n\t\t\t- [Extrait #1](#extrait-1)\n\t\t\t- [Extrait #2](#extrait-2)\n\t\t- [onlyOwner](#onlyowner)\n\t\t\t- [Accès aux fonctions concernées par le modifier **onlyOwner** :](#acc%C3%A8s-aux-fonctions-concern%C3%A9es-par-le-modifier-onlyowner-)\n\t\t\t- [Extrait](#extrait)\n\t\t- [onlyVoters](#onlyvoters)\n\t\t\t- [Accès aux fonctions concernées par le modifier **onlyVoters**](#acc%C3%A8s-aux-fonctions-concern%C3%A9es-par-le-modifier-onlyvoters)\n\t\t\t- [Extrait](#extrait)\n\t\t- [Processus de vote](#processus-de-vote)\n\t\t\t- [Scénario](#sc%C3%A9nario)\n\t\t- [Tests avec **Truffle**](#tests-avec-truffle)\n\t\t\t- [Versions des outils](#versions-des-outils)\n\t\t\t- [Résultat](#r%C3%A9sultat)\n\n\n\n### Enoncé\n\n*Oui, nous allons repartir du défi “Système de vote” !*\n\n*Vous repartirez du smart contract proposé en correction* \n\n*Depuis la dernière version, vous avez vu la partie CI/CD avec les tests et le déploiement.*\n\n*Vous devez alors fournir les tests unitaires de votre smart contract Nous n’attendons pas une couverture à 100% du smart contract mais veillez à bien tester les différentes possibilités de retours (event, revert).*\n\n**A rendre :**\n\n- votre lien Github (tests et explication rapide de ce qui a été fait dans un **readme.md**)\n\n\n##### Lien énoncé\n[https://formation.alyra.fr/products/developpeur-blockchain/categories/2149101531/posts/2153206159](https://formation.alyra.fr/products/developpeur-blockchain/categories/2149101531/posts/2153206159)\n\n##### Smart contract proposé\n[https://github.com/BenBktech/Promo-Buterin/blob/main/1.Solidity/Voting.sol](https://github.com/BenBktech/Promo-Buterin/blob/main/1.Solidity/Voting.sol)\n\n\n\n## Explications\n\n### Constantes\n\nAfin de gagner en lisibilité de lecture du script de test, différentes constantes sont définies.\n\nLes différents états du **Workflow** sont définis ainsi :\n\n##### Extrait #1\n\n```javascript\nconst RegisteringVoters            = BN(0);\nconst ProposalsRegistrationStarted = BN(1);\nconst ProposalsRegistrationEnded   = BN(2);\nconst VotingSessionStarted         = BN(3);\nconst VotingSessionEnded           = BN(4);\nconst VotesTallied                 = BN(5);\n```\n\nLes différentes adresses utilisées dans le script de test, sont aussi définies de manière explicites.\n\n##### Extrait #2\n\n```javascript\nconst OWNER   = accounts[0];\nconst VOTER_1 = accounts[1];\nconst VOTER_2 = accounts[2];\nconst VOTER_3 = accounts[3];\n```\n\nDe même que les différents indexs de proposition et autres constantes.\n\n##### Extrait #3\n\n```javascript\nconst INDEX_GENESIS         = BN(0);\nconst INDEX_PROPOSAL_1      = BN(1);\nconst INDEX_PROPOSAL_WINNER = BN(1);\nconst NN_VOTE               = BN(2);\n```\n\n\n### Les différents vecteurs d'états \u0026 variables\n\nOn peut noter les principaux **vecteurs d'états** dans le code du fichier source \"*Voting.sol*\"\n\n- WorkflowStatus\n- onlyOwner\n- onlyVoters\n\nAinsi que les **variables** qui évoluent en fonctions du déroulement du processus de vote et des intéractions réalisées par le \"*owner*\" et les votants.\n\n- winningProposalID\n- proposalsArray\n- voters\n\nLe cas **proposalsArray** est intéressant car à partir du passage à l'étape **ProposalsRegistrationStarted** du déroulement du processus de vote (workflow) il y a **au moins une** proposition - la proposition **GENESIS** - il y a donc un état testable !\n\n##### Extrait\n```javascript\nlet proposalStruct = await voting.getOneProposal(0, {from: _voter1});\nassert.equal(proposalStruct.description, \"GENESIS\",  \"Not GENESIS proposal\");\n```\n\n\n### Factorisation\n\nDes fonctions ont été également réalisées afin de \"*regrouper*\" des contrôles multiples et systématiques autour de certaines fonctionalités ou processus comme :\n\n- Les changements d'état dans l'**évolution du processus de vote**.\n  - exceptDefinedStatus()\n  - expectStatusChangeOk()\n  - expectStatusScheduling()\n  \n- L'**ajout d'un votant** légitime :\n  - expectAddNewVoter\n\n- Une fonction \"*couteau suisse*\" **checkGetVoterAndGetProposal()** éffectuant des contrôles sur un votant et la proposition votée, selon :\n  - Qu'une proposition de vote soit disponible ou pas\n  - Qu'un votant ait voté pour une proposition donnée\n\n\n##### Extraits\n\n```javascript\nasync function exceptDefinedStatus( _voting, _status) {\n\tworkflowStatus = (await _voting.workflowStatus());\n\texpect(workflowStatus).to.be.bignumber.equal(_status);\n}\n\nasync function expectStatusChangeOk( _voting, _prevStatus, _newStatus, _func) {\n\texpectEvent(\n\t\t_func,\n\t\t\"WorkflowStatusChange\", {\n\t\t\tpreviousStatus: _prevStatus,\n\t\t\tnewStatus     : _newStatus,\n\t\t}\n\t);\n\texceptDefinedStatus( _voting, _newStatus);\n}\n```\n\n```javascript\nasync function expectAddNewVoter( _voting, _address, _owner) {\n\texpectEvent(\n\t\tawait _voting.addVoter( _address, {from: _owner}),\n\t\t\"VoterRegistered\",\n\t\t{voterAddress: _address}\n\t);\n\n\tvoterStruct = await _voting.getVoter( _address, {from: _address});\n\texpect(voterStruct.isRegistered).to.be.true;\n\texpect(voterStruct.hasVoted).to.be.false;\n\texpect(voterStruct.votedProposalId).to.be.bignumber.equal( BN(0));\n\n}\n```\n\n\n### Déploiement\n\nAu déploiement du **contrat**, celui-ci : \n- ne possède ni votant\n- ne possède ni proposition de vote\n- ne possède aucun résultat de vote\n- il est juste \"*possédé*\" par le **owner** qui l'a déployé\n- l'état initiale du Workflow est **RegisteringVoters**\n\n##### Extrait\n```javascript\nit(\"Ownership\", async () =\u003e {\n\texpect( await voting.owner()).to.be.bignumber.equal( BN(OWNER));\n});\n\nit(\"Initial workflow status\", async () =\u003e {\n\texceptDefinedStatus( voting, RegisteringVoters);\n});\n\nit(\"No voter\", async () =\u003e {\n\tawait expectRevert(\n\t\tvoting.getVoter(OWNER, {from: OWNER}),\n\t\t\"You're not a voter\"\n\t);\n\n\tawait expectRevert(\n\t\tvoting.getVoter(VOTER_1, {from: OWNER}),\n\t\t\"You're not a voter\"\n\t);\n\n});\n\nit(\"No proposal\", async () =\u003e {\n\t// No registered voter, so no access to getOneProposal()\n\tawait expectRevert(\n\t\tvoting.getOneProposal(0, {from: OWNER}),\n\t\t\"You're not a voter\"\n\t);\n\tawait expectRevert(\n\t\tvoting.getOneProposal(0, {from: VOTER_1}),\n\t\t\"You're not a voter\"\n\t);\n\n});\n\nit(\"No result\", async () =\u003e {\n\n\t// No result !\n\tawait expectRevert(\n\t\tvoting.tallyVotes( {from: OWNER}),\n\t\t\"Current status is not voting session ended\"\n\t);\n\n\tlet winningProposalID = (await voting.winningProposalID( {from: OWNER}));\n\texpect(winningProposalID).to.be.bignumber.equal(BN(0));\n\n});\n```\n\n\n### Évolution du processus de vote\n\nLe déroulement du processus de vote, se fait dans une ordre bien défini, de *RegisteringVoters* vers *VotesTallied* (voir tableau plus bas) un **revert** se produit en cas de mauvais enchainement d'états.\n\nDes events précis - **WorkflowStatusChange()** - sont émis lors des changements d'étape.\n\n\n##### Les étapes du déroulement du vote sont les suivantes\n\n| WorkflowStatus               | Descriptions                 |\n| ---------------------------- | ---------------------------- |\n| RegisteringVoters            | Enregistrement des électeurs |\n| ProposalsRegistrationStarted | On récolte les propositions  |\n| ProposalsRegistrationEnded   | On clos les propositions     |\n| VotingSessionStarted         | Le vote est commencé         |\n| VotingSessionEnded           | Le vote est clos             |\n| VotesTallied                 | Le dépouillement est fait    |\n\n\n##### Extrait #1\n```javascript\nexpectEvent(\n\tawait voting.startProposalsRegistering(),\n\t\"WorkflowStatusChange\", {\n\t\tpreviousStatus: RegisteringVoters,\n\t\tnewStatus     : ProposalsRegistrationStarted,\n\t}\n);\n```\n##### Extrait #2\n```javascript\nawait expectRevert(\n\tvoting.endVotingSession(),\n\t\"Voting session havent started yet\"\n);\n```\n\n\n### onlyOwner\n\nLes fonctions en accès onlyOwner sont pour la grosse majorité liées à l'évolution de processus de vote, à l'exception de **addVoter()**.\n\n\n##### Accès aux fonctions concernées par le modifier **onlyOwner** :\n\n| WorkflowStatus               | addVoter | startProposalsRegistering | endProposalsRegistering | startVotingSession | endVotingSession | tallyVotes |\n| ---------------------------- | -------- | ------------------------- | ----------------------- | ------------------ | ---------------- | ---------- |\n| RegisteringVoters            | ✅       | ✅                        |                         |                    |                  |            |\n| ProposalsRegistrationStarted |          |                           | ✅                      |                    |                  |            |\n| ProposalsRegistrationEnded   |          |                           |                         | ✅                 |                  |            |\n| VotingSessionStarted         |          |                           |                         |                    | ✅               |            |\n| VotingSessionEnded           |          |                           |                         |                    |                  | ✅         |\n| VotesTallied                 |          |                           |                         |                    |                  |            |\n\n\n##### Extrait\n```javascript\nit(\"If not owner\", async () =\u003e {\n\n\tawait expectRevert(\n\t\tvoting.addVoter( VOTER_3, {from: VOTER_1}),\n\t\t\"caller is not the owner\"\n\t);\n\t\n\tawait expectRevert(\n\t\tvoting.startProposalsRegistering( {from: VOTER_1}),\n\t\t\"caller is not the owner\"\n\t);\n\n\tawait expectRevert(\n\t\tvoting.endProposalsRegistering( {from: VOTER_1}),\n\t\t\"caller is not the owner\"\n\t);\n\n\tawait expectRevert(\n\t\tvoting.startVotingSession( {from: VOTER_1}),\n\t\t\"caller is not the owner\"\n\t);\n\n\tawait expectRevert(\n\t\tvoting.endVotingSession( {from: VOTER_1}),\n\t\t\"caller is not the owner\"\n\t);\n\n\tawait expectRevert(\n\t\tvoting.tallyVotes( {from: VOTER_1}),\n\t\t\"caller is not the owner\"\n\t);\n\n});\n\n\nit(\"If owner\", async () =\u003e {\n\tawait expectAddNewVoter( voting, VOTER_1, OWNER);\n\tawait expectStatusScheduling( voting);\t\n});\n```\n\n\n### onlyVoters\n\nConcernant le vecteur d'états **onlyVoters**, l'accès aux fonctions *getVoter()* et *getOneProposal()* sont testables sans condition particulière autre que **onlyVoters**.\n\n*addProposal()* et *setVote()* nécessite par contre des états particuliers, pour être testés plus profondément. États liés au déroulement du processus de vote (*WorkflowStatus*).\n\n\n##### Accès aux fonctions concernées par le modifier **onlyVoters**\n\n| WorkflowStatus               | getVoter | getOneProposal | addProposal | setVote |\n| ---------------------------- | -------- | -------------- | ----------- | ------- |\n| RegisteringVoters            | ✅       | ✅             |             |         |\n| ProposalsRegistrationStarted | ✅       | ✅             | ✅          |         |\n| ProposalsRegistrationEnded   | ✅       | ✅             |             |         |\n| VotingSessionStarted         | ✅       | ✅             |             | ✅      |\n| VotingSessionEnded           | ✅       | ✅             |             |         |\n| VotesTallied                 | ✅       | ✅             |             |         |\n\n\n##### Extrait\n```javascript\nexpectEvent(\n\tawait voting.setVote( INDEX_PROPOSAL_1, {from: VOTER_1}),\n\t\"Voted\", {\n\t\tvoter: VOTER_1,\n\t\tproposalId: INDEX_PROPOSAL_1\n\t}\n);\n\n// voter2 attempt to vote, and fail\nawait expectRevert(\n\tvoting.setVote( INDEX_PROPOSAL_1, {from: VOTER_2}),\n\t\"You're not a voter\"\n);\n```\n\n### Processus de vote\n\n##### Scénario\n- Deux votants (d'adresses **VOTER_1** et **VOTER_3**) sont ajoutés à la session de vote.\n- La période de proposition est ouverte, tandis que l'ajout de votant est clot.\n- **VOTER_1** fait une proposition de vote (proposition #1, indice 1), La proposition d'indice 0 étant la **GENESIS** depuis l'ouverture des propostions.\n- **VOTER_1** fait à nouveau une proposition de vote, en vain.\n- De même pour **VOTER_2** qui n'est même pas un votant enregistré.\n- La période de proposition est close.\n- La période de vote commence pour les votants enregistrés.\n- **VOTER_1** vote pour la proposition #1, avec succès.\n- **VOTER_2** vote pour la proposition #1, en vain.\n- **VOTER_3** vote pour la proposition #1, avec succès.\n- Il y a maintenant **2 votes** pour la **proposition #1**\n- Après cloture des votes, le dépouillement est lancé.\n- Le proposition **gagnante** est la **proposition #1** (d'indice 1) avec **2 votes**\n\n\n### Tests avec **Truffle**\n\nLe test du code a été réalisé l'aide des outils suivant :\n- **Truffle** : [https://trufflesuite.com/](https://trufflesuite.com/)\n- **Ganache** : [https://trufflesuite.com/ganache/](https://trufflesuite.com/ganache/)\n\n- eth-gas-reporter : [https://www.npmjs.com/package/eth-gas-reporter](https://www.npmjs.com/package/eth-gas-reporter)\n\n\n##### Versions des outils\n```bash\nTruffle v5.9.2 (core: 5.9.2)\nGanache v7.8.0\nSolidity - 0.8.13 (solc-js)\nNode v18.16.0\nWeb3.js v1.10.0\n```\n\n##### Résultat\n\n![](2023-06-11-16-41-18.png)","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaugharne%2Ftestvote","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flaugharne%2Ftestvote","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaugharne%2Ftestvote/lists"}