@@ -2,6 +2,7 @@ package stakingindex
22
33import  (
44	"context" 
5+ 	"fmt" 
56	"math/big" 
67	"testing" 
78
@@ -60,3 +61,154 @@ func TestCandidateVotes(t *testing.T) {
6061		require .EqualValues (120 , newVotes .Sub (newVotes , originCandVotes ).Uint64 ())
6162	})
6263}
64+ 
65+ func  TestCandidateVotesInterface (t  * testing.T ) {
66+ 	r  :=  require .New (t )
67+ 
68+ 	cvs  :=  []CandidateVotes {
69+ 		newCandidateVotesWithBuffer (newCandidateVotes ()),
70+ 		newCandidateVotesWrapper (newCandidateVotesWithBuffer (newCandidateVotes ())),
71+ 		newCandidateVotesWrapperCommitInClone (newCandidateVotesWithBuffer (newCandidateVotes ())),
72+ 	}
73+ 	g  :=  genesis .TestDefault ()
74+ 	ctx  :=  genesis .WithGenesisContext (context .Background (), g )
75+ 	ctxBeforeRedsea  :=  protocol .MustGetFeatureCtx (protocol .WithFeatureCtx (protocol .WithBlockCtx (ctx , protocol.BlockCtx {BlockHeight : g .RedseaBlockHeight  -  1 })))
76+ 	ctxAfterRedsea  :=  protocol .MustGetFeatureCtx (protocol .WithFeatureCtx (protocol .WithBlockCtx (ctx , protocol.BlockCtx {BlockHeight : g .RedseaBlockHeight })))
77+ 	for  _ , cv  :=  range  cvs  {
78+ 		t .Run (fmt .Sprintf ("%T" , cv ), func (t  * testing.T ) {
79+ 			// not exist candidate 
80+ 			r .Nil (cv .Votes (ctxAfterRedsea , "notexist" ))
81+ 			r .Nil (cv .Votes (ctxBeforeRedsea , "notexist" ))
82+ 			r .False (cv .IsDirty ())
83+ 
84+ 			// add candidate 
85+ 			adds  :=  []struct  {
86+ 				cand    string 
87+ 				amount  string 
88+ 				votes   string 
89+ 			}{
90+ 				{"candidate1" , "100" , "120" },
91+ 				{"candidate2" , "200" , "240" },
92+ 				{"candidate1" , "300" , "360" },
93+ 				{"candidate3" , "400" , "480" },
94+ 				{"candidate2" , "500" , "600" },
95+ 				{"candidate2" , "-200" , "-240" },
96+ 			}
97+ 			expects  :=  []struct  {
98+ 				cand    string 
99+ 				amount  string 
100+ 				votes   string 
101+ 			}{
102+ 				{"candidate1" , "400" , "480" },
103+ 				{"candidate2" , "500" , "600" },
104+ 				{"candidate3" , "400" , "480" },
105+ 			}
106+ 			for  _ , add  :=  range  adds  {
107+ 				amount  :=  big .NewInt (0 )
108+ 				votes  :=  big .NewInt (0 )
109+ 				_ , ok  :=  amount .SetString (add .amount , 10 )
110+ 				r .True (ok )
111+ 				_ , ok  =  votes .SetString (add .votes , 10 )
112+ 				r .True (ok )
113+ 				cv .Add (add .cand , amount , votes )
114+ 			}
115+ 			checkVotes  :=  func (cv  CandidateVotes ) {
116+ 				for  _ , expect  :=  range  expects  {
117+ 					amount  :=  big .NewInt (0 )
118+ 					votes  :=  big .NewInt (0 )
119+ 					_ , ok  :=  amount .SetString (expect .amount , 10 )
120+ 					r .True (ok )
121+ 					_ , ok  =  votes .SetString (expect .votes , 10 )
122+ 					r .True (ok )
123+ 					r .Equal (amount , cv .Votes (ctxBeforeRedsea , expect .cand ))
124+ 					r .Equal (votes , cv .Votes (ctxAfterRedsea , expect .cand ))
125+ 				}
126+ 			}
127+ 			checkVotes (cv )
128+ 			cl  :=  cv .Clone ()
129+ 			checkVotes (cl )
130+ 			// both cv and cl are dirty 
131+ 			r .True (cv .IsDirty ())
132+ 			r .True (cl .IsDirty ())
133+ 			// serialize dirty cv should fail 
134+ 			_ , err  :=  cv .Serialize ()
135+ 			r .ErrorIs (err , ErrCandidateVotesIsDirty )
136+ 			// commit cv 
137+ 			reset  :=  cv .Commit ()
138+ 			r .False (reset .IsDirty ())
139+ 			data , err  :=  reset .Serialize ()
140+ 			r .NoError (err )
141+ 			// deserialize to new cv 
142+ 			decv  :=  newCandidateVotes ()
143+ 			err  =  decv .Deserialize (data )
144+ 			r .NoError (err )
145+ 			checkVotes (newCandidateVotesWithBuffer (decv ))
146+ 			// adds not affect base 
147+ 			cv .Add ("candidate4" , big .NewInt (1000 ), big .NewInt (1200 ))
148+ 			cv .Add ("candidate1" , big .NewInt (- 100 ), big .NewInt (- 120 ))
149+ 			cv .Add ("candidate2" , big .NewInt (100 ), big .NewInt (120 ))
150+ 			checkVotes (cv .Base ())
151+ 		})
152+ 	}
153+ }
154+ 
155+ func  TestCandidateVotesWrapper (t  * testing.T ) {
156+ 	r  :=  require .New (t )
157+ 	baseCv  :=  newCandidateVotesWithBuffer (newCandidateVotes ())
158+ 	baseCv .Add ("candidate1" , big .NewInt (100 ), big .NewInt (120 ))
159+ 	baseCv .Add ("candidate2" , big .NewInt (200 ), big .NewInt (240 ))
160+ 	baseCv .Add ("candidate3" , big .NewInt (400 ), big .NewInt (480 ))
161+ 	base  :=  baseCv .Commit ()
162+ 	// wrap's changes should not affect base 
163+ 	wrap  :=  newCandidateVotesWrapper (base )
164+ 	wrap .Add ("candidate1" , big .NewInt (300 ), big .NewInt (360 ))
165+ 	wrap .Add ("candidate4" , big .NewInt (1000 ), big .NewInt (1200 ))
166+ 	candidateVotesEqual (r , base , wrap .Base (), []string {"candidate1" , "candidate2" , "candidate3" , "candidate4" })
167+ 	// multiple wraps return base recursively 
168+ 	wrap2  :=  newCandidateVotesWrapper (wrap )
169+ 	wrap2 .Add ("candidate2" , big .NewInt (500 ), big .NewInt (600 ))
170+ 	wrap2 .Add ("candidate5" , big .NewInt (2000 ), big .NewInt (2400 ))
171+ 	candidateVotesEqual (r , base , wrap2 .Base (), []string {"candidate1" , "candidate2" , "candidate3" , "candidate4" , "candidate5" })
172+ 	// commit wrap should apply all changes to base 
173+ 	wrap2 .Commit ()
174+ 	candidateVotesEqual (r , base , wrap2 , []string {"candidate1" , "candidate2" , "candidate3" , "candidate4" , "candidate5" })
175+ }
176+ 
177+ func  TestCandidateVotesWrapperCommitInClone (t  * testing.T ) {
178+ 	r  :=  require .New (t )
179+ 	baseCv  :=  newCandidateVotesWithBuffer (newCandidateVotes ())
180+ 	baseCv .Add ("candidate1" , big .NewInt (100 ), big .NewInt (120 ))
181+ 	baseCv .Add ("candidate2" , big .NewInt (200 ), big .NewInt (240 ))
182+ 	baseCv .Add ("candidate3" , big .NewInt (400 ), big .NewInt (480 ))
183+ 	base  :=  baseCv .Commit ()
184+ 	wrap  :=  newCandidateVotesWrapper (base )
185+ 	wrap .Add ("candidate1" , big .NewInt (300 ), big .NewInt (360 ))
186+ 	wrap .Add ("candidate4" , big .NewInt (1000 ), big .NewInt (1200 ))
187+ 	wrap2  :=  newCandidateVotesWrapper (wrap )
188+ 	wrap2 .Add ("candidate2" , big .NewInt (500 ), big .NewInt (600 ))
189+ 	wrap2 .Add ("candidate5" , big .NewInt (2000 ), big .NewInt (2400 ))
190+ 	wrap3  :=  newCandidateVotesWrapper (wrap )
191+ 	wrap3 .Add ("candidate3" , big .NewInt (700 ), big .NewInt (840 ))
192+ 	wrap3 .Add ("candidate1" , big .NewInt (- 3000 ), big .NewInt (- 3600 ))
193+ 	wrap3Clone  :=  wrap3 .Clone ()
194+ 
195+ 	// base return the first base 
196+ 	wrap4  :=  newCandidateVotesWrapperCommitInClone (wrap3 )
197+ 	wrap4 .Add ("candidate2" , big .NewInt (3000 ), big .NewInt (3600 ))
198+ 	wrap4 .Add ("candidate3" , big .NewInt (4000 ), big .NewInt (4800 ))
199+ 	candidateVotesEqual (r , wrap4 .Base (), wrap3 , []string {"candidate1" , "candidate2" , "candidate3" , "candidate4" , "candidate5" })
200+ 	// commit wrap4 should not apply all changes to base 
201+ 	wrap4 .Commit ()
202+ 	candidateVotesEqual (r , wrap3 , wrap3Clone , []string {"candidate1" , "candidate2" , "candidate3" , "candidate4" , "candidate5" })
203+ }
204+ 
205+ func  candidateVotesEqual (r  * require.Assertions , cv1 , cv2  CandidateVotes , cands  []string ) {
206+ 	g  :=  genesis .TestDefault ()
207+ 	ctx  :=  genesis .WithGenesisContext (context .Background (), g )
208+ 	ctxBeforeRedsea  :=  protocol .MustGetFeatureCtx (protocol .WithFeatureCtx (protocol .WithBlockCtx (ctx , protocol.BlockCtx {BlockHeight : g .RedseaBlockHeight  -  1 })))
209+ 	ctxAfterRedsea  :=  protocol .MustGetFeatureCtx (protocol .WithFeatureCtx (protocol .WithBlockCtx (ctx , protocol.BlockCtx {BlockHeight : g .RedseaBlockHeight })))
210+ 	for  _ , cand  :=  range  cands  {
211+ 		r .Equal (cv1 .Votes (ctxBeforeRedsea , cand ), cv2 .Votes (ctxBeforeRedsea , cand ))
212+ 		r .Equal (cv1 .Votes (ctxAfterRedsea , cand ), cv2 .Votes (ctxAfterRedsea , cand ))
213+ 	}
214+ }
0 commit comments