You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

transaction_executor_test.go 14 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package tm
  18. import (
  19. "context"
  20. "fmt"
  21. "reflect"
  22. "testing"
  23. "time"
  24. "github.com/agiledragon/gomonkey/v2"
  25. "github.com/pkg/errors"
  26. "github.com/stretchr/testify/assert"
  27. "seata.apache.org/seata-go/pkg/protocol/message"
  28. )
  29. func TestTransactionExecutorBegin(t *testing.T) {
  30. type Test struct {
  31. ctx context.Context
  32. gc *GtxConfig
  33. xid string
  34. wantHasMock bool
  35. wantMockTargetName string
  36. wantMockFunction interface{}
  37. wantHasError bool
  38. wantErrorString string
  39. wantUseExist bool
  40. wantBeginNew bool
  41. }
  42. gts := []Test{
  43. {
  44. ctx: context.Background(),
  45. gc: &GtxConfig{
  46. Name: "MockGtxName",
  47. Propagation: NotSupported,
  48. },
  49. xid: "123456",
  50. },
  51. {
  52. ctx: context.Background(),
  53. gc: &GtxConfig{
  54. Name: "MockGtxName",
  55. Propagation: Supports,
  56. },
  57. xid: "123456",
  58. wantUseExist: true,
  59. },
  60. {
  61. ctx: context.Background(),
  62. gc: &GtxConfig{
  63. Name: "MockGtxName",
  64. Propagation: RequiresNew,
  65. },
  66. xid: "123456",
  67. wantHasMock: true,
  68. wantMockTargetName: "Begin",
  69. wantMockFunction: func(_ *GlobalTransactionManager, ctx context.Context, i time.Duration) error {
  70. SetXID(ctx, "123456")
  71. return nil
  72. },
  73. wantBeginNew: true,
  74. },
  75. // use exist
  76. {
  77. ctx: context.Background(),
  78. gc: &GtxConfig{
  79. Name: "MockGtxName",
  80. Propagation: Required,
  81. },
  82. xid: "123456",
  83. wantUseExist: true,
  84. },
  85. //Begin new
  86. {
  87. ctx: context.Background(),
  88. gc: &GtxConfig{
  89. Name: "MockGtxName",
  90. Propagation: Required,
  91. },
  92. wantHasMock: true,
  93. wantMockTargetName: "Begin",
  94. wantMockFunction: func(_ *GlobalTransactionManager, ctx context.Context, i time.Duration) error {
  95. SetXID(ctx, "123456")
  96. return nil
  97. },
  98. wantBeginNew: true,
  99. },
  100. // has error
  101. {
  102. ctx: context.Background(),
  103. gc: &GtxConfig{
  104. Name: "MockGtxName",
  105. Propagation: Never,
  106. },
  107. xid: "123456",
  108. wantHasError: true,
  109. wantErrorString: "existing transaction found for transaction marked with pg 'never', xid = 123456",
  110. },
  111. // has not error
  112. {
  113. ctx: context.Background(),
  114. gc: &GtxConfig{
  115. Name: "MockGtxName",
  116. Propagation: Never,
  117. },
  118. },
  119. // use exist
  120. {
  121. ctx: context.Background(),
  122. gc: &GtxConfig{
  123. Name: "MockGtxName",
  124. Propagation: Mandatory,
  125. },
  126. xid: "123456",
  127. wantUseExist: true,
  128. },
  129. // has error
  130. {
  131. ctx: context.Background(),
  132. gc: &GtxConfig{
  133. Name: "MockGtxName",
  134. Propagation: Mandatory,
  135. },
  136. wantHasError: true,
  137. wantErrorString: "no existing transaction found for transaction marked with pg 'mandatory'",
  138. },
  139. // default case
  140. {
  141. ctx: context.Background(),
  142. gc: &GtxConfig{
  143. Name: "MockGtxName",
  144. Propagation: -1,
  145. },
  146. wantHasError: true,
  147. wantErrorString: "not Supported Propagation:-1",
  148. },
  149. }
  150. for i, v := range gts {
  151. t.Logf("Case %v: %+v", i, v)
  152. var stub *gomonkey.Patches
  153. // set up stub
  154. if v.wantHasMock {
  155. stub = gomonkey.ApplyMethod(reflect.TypeOf(GetGlobalTransactionManager()), v.wantMockTargetName, v.wantMockFunction)
  156. }
  157. v.ctx = InitSeataContext(v.ctx)
  158. if v.xid != "" {
  159. SetXID(v.ctx, v.xid)
  160. }
  161. err := begin(v.ctx, v.gc)
  162. if v.wantHasError {
  163. assert.NotNil(t, err)
  164. } else {
  165. assert.Nil(t, err)
  166. }
  167. if v.wantBeginNew {
  168. assert.Equal(t, Launcher, *GetTxRole(v.ctx))
  169. }
  170. if v.wantUseExist {
  171. assert.Equal(t, Participant, *GetTxRole(v.ctx))
  172. }
  173. // rest up stub
  174. if v.wantHasMock {
  175. stub.Reset()
  176. }
  177. }
  178. }
  179. func TestTransactionExecutorCommit(t *testing.T) {
  180. ctx := context.Background()
  181. ctx = InitSeataContext(ctx)
  182. SetTxRole(ctx, Launcher)
  183. SetTxStatus(ctx, message.GlobalStatusBegin)
  184. SetXID(ctx, "")
  185. assert.Equal(t, "Commit xid should not be empty", commitOrRollback(ctx, true).Error())
  186. }
  187. func TestTransactionExecurotRollback(t *testing.T) {
  188. ctx := context.Background()
  189. ctx = InitSeataContext(ctx)
  190. SetTxRole(ctx, Launcher)
  191. SetTxStatus(ctx, message.GlobalStatusBegin)
  192. SetXID(ctx, "")
  193. errActual := commitOrRollback(ctx, false)
  194. assert.Equal(t, "Rollback xid should not be empty", errActual.Error())
  195. }
  196. func TestCommitOrRollback(t *testing.T) {
  197. type Test struct {
  198. ctx context.Context
  199. tx GlobalTransaction
  200. ok bool
  201. wantHasMock bool
  202. wantMockTargetName string
  203. wantMockFunction interface{}
  204. wantHasError bool
  205. wantErrorString string
  206. }
  207. gts := []Test{
  208. {
  209. ctx: context.Background(),
  210. tx: GlobalTransaction{
  211. TxRole: UnKnow,
  212. },
  213. wantHasError: true,
  214. wantErrorString: "global transaction role is UnKnow.",
  215. },
  216. //ok with nil
  217. {
  218. ctx: context.Background(),
  219. tx: GlobalTransaction{
  220. TxRole: Launcher,
  221. },
  222. ok: true,
  223. wantHasMock: true,
  224. wantMockTargetName: "Commit",
  225. wantMockFunction: func(_ *GlobalTransactionManager, ctx context.Context, gtr *GlobalTransaction) error {
  226. return nil
  227. },
  228. },
  229. //ok with error
  230. {
  231. ctx: context.Background(),
  232. tx: GlobalTransaction{
  233. TxRole: Launcher,
  234. },
  235. ok: true,
  236. wantHasMock: true,
  237. wantMockTargetName: "Commit",
  238. wantMockFunction: func(_ *GlobalTransactionManager, ctx context.Context, gtr *GlobalTransaction) error {
  239. return errors.New("Mock error")
  240. },
  241. wantHasError: true,
  242. wantErrorString: "Mock error",
  243. },
  244. // false with nil
  245. {
  246. ctx: context.Background(),
  247. tx: GlobalTransaction{
  248. TxRole: Launcher,
  249. },
  250. ok: false,
  251. wantHasMock: true,
  252. wantMockTargetName: "Rollback",
  253. wantMockFunction: func(_ *GlobalTransactionManager, ctx context.Context, gtr *GlobalTransaction) error {
  254. return nil
  255. },
  256. },
  257. // false with error
  258. {
  259. ctx: context.Background(),
  260. tx: GlobalTransaction{
  261. TxRole: Launcher,
  262. },
  263. ok: false,
  264. wantHasMock: true,
  265. wantMockTargetName: "Rollback",
  266. wantMockFunction: func(_ *GlobalTransactionManager, ctx context.Context, gtr *GlobalTransaction) error {
  267. return errors.New("Mock error")
  268. },
  269. wantHasError: true,
  270. wantErrorString: "Mock error",
  271. },
  272. {
  273. ctx: context.Background(),
  274. tx: GlobalTransaction{
  275. TxRole: Participant,
  276. },
  277. },
  278. }
  279. for i, v := range gts {
  280. t.Logf("Case %v: %+v", i, v)
  281. v.ctx = InitSeataContext(v.ctx)
  282. SetTx(v.ctx, &v.tx)
  283. var stub *gomonkey.Patches
  284. if v.wantHasMock {
  285. stub = gomonkey.ApplyMethod(reflect.TypeOf(GetGlobalTransactionManager()), v.wantMockTargetName, v.wantMockFunction)
  286. }
  287. err := commitOrRollback(v.ctx, v.ok)
  288. if v.wantHasError {
  289. assert.Equal(t, v.wantErrorString, err.Error())
  290. } else {
  291. assert.Nil(t, err)
  292. }
  293. if v.wantHasMock {
  294. stub.Reset()
  295. }
  296. }
  297. }
  298. func TestClearTxConf(t *testing.T) {
  299. ctx := InitSeataContext(context.Background())
  300. SetTx(ctx, &GlobalTransaction{
  301. Xid: "123456",
  302. TxName: "MockTxName",
  303. TxStatus: message.GlobalStatusBegin,
  304. TxRole: Launcher,
  305. })
  306. clearTxConf(ctx)
  307. assert.Equal(t, "123456", GetXID(ctx))
  308. assert.Equal(t, UnKnow, *GetTxRole(ctx))
  309. assert.Equal(t, message.GlobalStatusUnKnown, *GetTxStatus(ctx))
  310. assert.Equal(t, "", GetTxName(ctx))
  311. }
  312. func TestUseExistGtx(t *testing.T) {
  313. ctx := InitSeataContext(context.Background())
  314. SetXID(ctx, "123456")
  315. useExistGtx(ctx, &GtxConfig{
  316. Name: "useExistGtxMock",
  317. })
  318. assert.Equal(t, Participant, *GetTxRole(ctx))
  319. assert.Equal(t, message.GlobalStatusBegin, *GetTxStatus(ctx))
  320. }
  321. func TestBeginNewGtx(t *testing.T) {
  322. ctx := InitSeataContext(context.Background())
  323. g := &GtxConfig{
  324. Name: "beginNewGtxMock",
  325. }
  326. // case return nil
  327. gomonkey.ApplyMethod(reflect.TypeOf(GetGlobalTransactionManager()), "Begin",
  328. func(_ *GlobalTransactionManager, ctx context.Context, timeout time.Duration) error {
  329. return nil
  330. })
  331. assert.Nil(t, beginNewGtx(ctx, g))
  332. assert.Equal(t, Launcher, *GetTxRole(ctx))
  333. assert.Equal(t, g.Name, GetTxName(ctx))
  334. assert.Equal(t, message.GlobalStatusBegin, *GetTxStatus(ctx))
  335. // case return error
  336. err := errors.New("Mock Error")
  337. gomonkey.ApplyMethod(reflect.TypeOf(GetGlobalTransactionManager()), "Begin",
  338. func(_ *GlobalTransactionManager, ctx context.Context, timeout time.Duration) error {
  339. return err
  340. })
  341. assert.Error(t, beginNewGtx(ctx, g))
  342. }
  343. func TestWithGlobalTx(t *testing.T) {
  344. callbackError := func(ctx context.Context) error {
  345. return errors.New("mock callback error")
  346. }
  347. callbackNil := func(ctx context.Context) error {
  348. return nil
  349. }
  350. type testCase struct {
  351. GtxConfig *GtxConfig
  352. occurError bool
  353. timeoutErr bool
  354. errMessage string
  355. callbackErr bool
  356. mockBeginFunc interface{}
  357. mockBeginTarget interface{}
  358. mockSecondPhaseFunc interface{}
  359. mockSecondPhaseTarget interface{}
  360. mockRollbackTargetName string
  361. mockRollbackFunc interface{}
  362. mockTimeoutTarget interface{}
  363. mockTimeoutFunc interface{}
  364. secondErr bool
  365. callback CallbackWithCtx
  366. }
  367. gts := []testCase{
  368. // case TxName is nil
  369. {
  370. GtxConfig: &GtxConfig{},
  371. occurError: true,
  372. errMessage: "global transaction name is required.",
  373. },
  374. // case GtxConfig is nil
  375. {
  376. occurError: true,
  377. errMessage: "global transaction config info is required.",
  378. },
  379. // case mock begin return error
  380. {
  381. GtxConfig: &GtxConfig{
  382. Name: "MockGtxConfig",
  383. },
  384. occurError: true,
  385. errMessage: "mock begin",
  386. mockBeginTarget: begin,
  387. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  388. return errors.New("mock begin")
  389. },
  390. },
  391. // case callback return error
  392. {
  393. GtxConfig: &GtxConfig{
  394. Name: "MockGtxConfig",
  395. },
  396. callbackErr: true,
  397. callback: callbackError,
  398. mockBeginTarget: begin,
  399. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  400. return nil
  401. },
  402. },
  403. // case callback return nil
  404. {
  405. GtxConfig: &GtxConfig{
  406. Name: "MockGtxConfig",
  407. },
  408. mockBeginTarget: begin,
  409. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  410. SetXID(ctx, "123456")
  411. return nil
  412. },
  413. callback: callbackNil,
  414. mockSecondPhaseTarget: commitOrRollback,
  415. mockSecondPhaseFunc: func(ctx context.Context, s bool) error {
  416. return nil
  417. },
  418. },
  419. // case second mock error
  420. {
  421. GtxConfig: &GtxConfig{
  422. Name: "MockGtxConfig",
  423. },
  424. mockBeginTarget: begin,
  425. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  426. SetXID(ctx, "123456")
  427. return nil
  428. },
  429. callback: callbackNil,
  430. mockSecondPhaseTarget: commitOrRollback,
  431. mockSecondPhaseFunc: func(ctx context.Context, s bool) error {
  432. return errors.New("second error mock")
  433. },
  434. secondErr: true,
  435. },
  436. // case tm detected a timeout and executed rollback successfully.
  437. {
  438. GtxConfig: &GtxConfig{
  439. Name: "MockGtxConfig",
  440. Timeout: time.Second * 30,
  441. },
  442. timeoutErr: false,
  443. mockBeginTarget: begin,
  444. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  445. SetXID(ctx, "123456")
  446. SetTxRole(ctx, Launcher)
  447. return nil
  448. },
  449. callback: callbackNil,
  450. mockTimeoutTarget: isTimeout,
  451. mockTimeoutFunc: func(ctx context.Context) bool {
  452. return true
  453. },
  454. mockRollbackTargetName: "Rollback",
  455. mockRollbackFunc: func(_ *GlobalTransactionManager, ctx context.Context, gtr *GlobalTransaction) error {
  456. return nil
  457. },
  458. },
  459. // tm detected a timeout but rollback threw an exception.
  460. {
  461. GtxConfig: &GtxConfig{
  462. Name: "MockGtxConfig",
  463. Timeout: time.Second * 30,
  464. },
  465. timeoutErr: true,
  466. errMessage: "tm detected a timeout but rollback threw an exception",
  467. mockBeginTarget: begin,
  468. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  469. SetXID(ctx, "123456")
  470. SetTxRole(ctx, Launcher)
  471. return nil
  472. },
  473. callback: callbackNil,
  474. mockTimeoutTarget: isTimeout,
  475. mockTimeoutFunc: func(ctx context.Context) bool {
  476. return true
  477. },
  478. mockRollbackTargetName: "Rollback",
  479. mockRollbackFunc: func(_ *GlobalTransactionManager, ctx context.Context, gtr *GlobalTransaction) error {
  480. return fmt.Errorf("tm detected a timeout but rollback threw an exception")
  481. },
  482. },
  483. }
  484. for i, v := range gts {
  485. t.Logf("Case %v: %+v", i, v)
  486. var beginStub *gomonkey.Patches
  487. var secondStub *gomonkey.Patches
  488. var timeoutStub *gomonkey.Patches
  489. var rollbackStub *gomonkey.Patches
  490. if v.mockBeginTarget != nil {
  491. beginStub = gomonkey.ApplyFunc(v.mockBeginTarget, v.mockBeginFunc)
  492. }
  493. if v.mockSecondPhaseTarget != nil {
  494. secondStub = gomonkey.ApplyFunc(v.mockSecondPhaseTarget, v.mockSecondPhaseFunc)
  495. }
  496. if v.mockTimeoutTarget != nil {
  497. timeoutStub = gomonkey.ApplyFunc(v.mockTimeoutTarget, v.mockTimeoutFunc)
  498. }
  499. if v.mockRollbackTargetName != "" {
  500. rollbackStub = gomonkey.ApplyMethod(reflect.TypeOf(GetGlobalTransactionManager()), v.mockRollbackTargetName, v.mockRollbackFunc)
  501. }
  502. ctx := context.Background()
  503. err := WithGlobalTx(ctx, v.GtxConfig, v.callback)
  504. if v.occurError {
  505. assert.Equal(t, v.errMessage, err.Error())
  506. }
  507. if v.callbackErr {
  508. assert.NotNil(t, err)
  509. }
  510. if v.secondErr {
  511. assert.NotNil(t, err)
  512. }
  513. if v.timeoutErr {
  514. assert.Regexp(t, v.errMessage, err.Error())
  515. }
  516. if v.mockBeginTarget != nil {
  517. beginStub.Reset()
  518. }
  519. if v.mockSecondPhaseTarget != nil {
  520. secondStub.Reset()
  521. }
  522. if v.mockTimeoutTarget != nil {
  523. timeoutStub.Reset()
  524. }
  525. if v.mockRollbackTargetName != "" {
  526. rollbackStub.Reset()
  527. }
  528. }
  529. }