@@ -56,6 +56,12 @@ impl PremiumCalculator {
5656 storage:: get_table ( & env) . map ( |t| t. version ) . unwrap_or ( 0 )
5757 }
5858
59+ /// Returns the semver version string stamped at build time from `Cargo.toml`.
60+ /// Read-only: no storage access, no auth required. Safe to call via simulation.
61+ pub fn version ( env : Env ) -> soroban_sdk:: String {
62+ soroban_sdk:: String :: from_str ( & env, env ! ( "CARGO_PKG_VERSION" ) )
63+ }
64+
5965 /// Admin: replace the multiplier table. Version must be strictly greater.
6066 pub fn update_table ( env : Env , new_table : MultiplierTable ) -> Result < ( ) , CalcError > {
6167 let admin = storage:: get_admin ( & env) . ok_or ( CalcError :: NotInitialized ) ?;
@@ -164,3 +170,50 @@ fn mul_ratio(amount: i128, num: i128, den: i128) -> Result<i128, CalcError> {
164170fn checked_sub ( a : i128 , b : i128 ) -> Result < i128 , CalcError > {
165171 a. checked_sub ( b) . ok_or ( CalcError :: Overflow )
166172}
173+
174+ // ── Tests ──────────────────────────────────────────────────────────────────────
175+
176+ #[ cfg( test) ]
177+ mod tests {
178+ use super :: * ;
179+ use soroban_sdk:: { testutils:: Address as _, Address , Env } ;
180+
181+ #[ test]
182+ fn version_returns_nonempty_semver_string ( ) {
183+ let env = Env :: default ( ) ;
184+ let contract_id = env. register ( PremiumCalculator , ( ) ) ;
185+ let client = PremiumCalculatorClient :: new ( & env, & contract_id) ;
186+
187+ let v = client. version ( ) ;
188+ let v_str = v. to_string ( ) ;
189+ assert ! ( !v_str. is_empty( ) , "version() must not be empty" ) ;
190+ assert_eq ! (
191+ v_str,
192+ env!( "CARGO_PKG_VERSION" ) ,
193+ "version() must match Cargo.toml"
194+ ) ;
195+ }
196+
197+ #[ test]
198+ fn version_requires_no_auth_and_no_init ( ) {
199+ // Contract is not initialised — version() must succeed regardless.
200+ let env = Env :: default ( ) ;
201+ let contract_id = env. register ( PremiumCalculator , ( ) ) ;
202+ let client = PremiumCalculatorClient :: new ( & env, & contract_id) ;
203+ let _ = client. version ( ) ; // must not panic
204+ }
205+
206+ #[ test]
207+ fn version_is_idempotent ( ) {
208+ let env = Env :: default ( ) ;
209+ env. mock_all_auths ( ) ;
210+ let contract_id = env. register ( PremiumCalculator , ( ) ) ;
211+ let client = PremiumCalculatorClient :: new ( & env, & contract_id) ;
212+ let admin = Address :: generate ( & env) ;
213+ client. initialize ( & admin) ;
214+
215+ let v1 = client. version ( ) ;
216+ let v2 = client. version ( ) ;
217+ assert_eq ! ( v1, v2) ;
218+ }
219+ }
0 commit comments