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 15 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  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 TestTransferTx(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. newCtx := transferTx(ctx)
  307. assert.Equal(t, "123456", GetXID(ctx))
  308. assert.Equal(t, Launcher, *GetTxRole(ctx))
  309. assert.Equal(t, message.GlobalStatusBegin, *GetTxStatus(ctx))
  310. assert.Equal(t, "MockTxName", GetTxName(ctx))
  311. assert.Equal(t, "123456", GetXID(newCtx))
  312. assert.Equal(t, UnKnow, *GetTxRole(newCtx))
  313. assert.Equal(t, message.GlobalStatusUnKnown, *GetTxStatus(newCtx))
  314. assert.Equal(t, "", GetTxName(newCtx))
  315. }
  316. func TestUseExistGtx(t *testing.T) {
  317. ctx := InitSeataContext(context.Background())
  318. SetXID(ctx, "123456")
  319. useExistGtx(ctx, &GtxConfig{
  320. Name: "useExistGtxMock",
  321. })
  322. assert.Equal(t, Participant, *GetTxRole(ctx))
  323. assert.Equal(t, message.GlobalStatusBegin, *GetTxStatus(ctx))
  324. }
  325. func TestBeginNewGtx(t *testing.T) {
  326. ctx := InitSeataContext(context.Background())
  327. g := &GtxConfig{
  328. Name: "beginNewGtxMock",
  329. }
  330. // case return nil
  331. gomonkey.ApplyMethod(reflect.TypeOf(GetGlobalTransactionManager()), "Begin",
  332. func(_ *GlobalTransactionManager, ctx context.Context, timeout time.Duration) error {
  333. return nil
  334. })
  335. assert.Nil(t, beginNewGtx(ctx, g))
  336. assert.Equal(t, Launcher, *GetTxRole(ctx))
  337. assert.Equal(t, g.Name, GetTxName(ctx))
  338. assert.Equal(t, message.GlobalStatusBegin, *GetTxStatus(ctx))
  339. // case return error
  340. err := errors.New("Mock Error")
  341. gomonkey.ApplyMethod(reflect.TypeOf(GetGlobalTransactionManager()), "Begin",
  342. func(_ *GlobalTransactionManager, ctx context.Context, timeout time.Duration) error {
  343. return err
  344. })
  345. assert.Error(t, beginNewGtx(ctx, g))
  346. }
  347. func TestWithGlobalTx(t *testing.T) {
  348. callbackError := func(ctx context.Context) error {
  349. return errors.New("mock callback error")
  350. }
  351. callbackNil := func(ctx context.Context) error {
  352. return nil
  353. }
  354. type testCase struct {
  355. GtxConfig *GtxConfig
  356. occurError bool
  357. timeoutErr bool
  358. errMessage string
  359. callbackErr bool
  360. mockBeginFunc interface{}
  361. mockBeginTarget interface{}
  362. mockSecondPhaseFunc interface{}
  363. mockSecondPhaseTarget interface{}
  364. mockRollbackTargetName string
  365. mockRollbackFunc interface{}
  366. mockTimeoutTarget interface{}
  367. mockTimeoutFunc interface{}
  368. secondErr bool
  369. callback CallbackWithCtx
  370. }
  371. gts := []testCase{
  372. // case TxName is nil
  373. {
  374. GtxConfig: &GtxConfig{},
  375. occurError: true,
  376. errMessage: "global transaction name is required.",
  377. },
  378. // case GtxConfig is nil
  379. {
  380. occurError: true,
  381. errMessage: "global transaction config info is required.",
  382. },
  383. // case mock begin return error
  384. {
  385. GtxConfig: &GtxConfig{
  386. Name: "MockGtxConfig",
  387. },
  388. occurError: true,
  389. errMessage: "mock begin",
  390. mockBeginTarget: begin,
  391. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  392. return errors.New("mock begin")
  393. },
  394. },
  395. // case callback return error
  396. {
  397. GtxConfig: &GtxConfig{
  398. Name: "MockGtxConfig",
  399. },
  400. callbackErr: true,
  401. callback: callbackError,
  402. mockBeginTarget: begin,
  403. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  404. return nil
  405. },
  406. },
  407. // case callback return nil
  408. {
  409. GtxConfig: &GtxConfig{
  410. Name: "MockGtxConfig",
  411. },
  412. mockBeginTarget: begin,
  413. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  414. SetXID(ctx, "123456")
  415. return nil
  416. },
  417. callback: callbackNil,
  418. mockSecondPhaseTarget: commitOrRollback,
  419. mockSecondPhaseFunc: func(ctx context.Context, s bool) error {
  420. return nil
  421. },
  422. },
  423. // case second mock error
  424. {
  425. GtxConfig: &GtxConfig{
  426. Name: "MockGtxConfig",
  427. },
  428. mockBeginTarget: begin,
  429. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  430. SetXID(ctx, "123456")
  431. return nil
  432. },
  433. callback: callbackNil,
  434. mockSecondPhaseTarget: commitOrRollback,
  435. mockSecondPhaseFunc: func(ctx context.Context, s bool) error {
  436. return errors.New("second error mock")
  437. },
  438. secondErr: true,
  439. },
  440. // case tm detected a timeout and executed rollback successfully.
  441. {
  442. GtxConfig: &GtxConfig{
  443. Name: "MockGtxConfig",
  444. Timeout: time.Second * 30,
  445. },
  446. timeoutErr: false,
  447. mockBeginTarget: begin,
  448. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  449. SetXID(ctx, "123456")
  450. SetTxRole(ctx, Launcher)
  451. return nil
  452. },
  453. callback: callbackNil,
  454. mockTimeoutTarget: isTimeout,
  455. mockTimeoutFunc: func(ctx context.Context) bool {
  456. return true
  457. },
  458. mockRollbackTargetName: "Rollback",
  459. mockRollbackFunc: func(_ *GlobalTransactionManager, ctx context.Context, gtr *GlobalTransaction) error {
  460. return nil
  461. },
  462. },
  463. // tm detected a timeout but rollback threw an exception.
  464. {
  465. GtxConfig: &GtxConfig{
  466. Name: "MockGtxConfig",
  467. Timeout: time.Second * 30,
  468. },
  469. timeoutErr: true,
  470. errMessage: "tm detected a timeout but rollback threw an exception",
  471. mockBeginTarget: begin,
  472. mockBeginFunc: func(ctx context.Context, gc *GtxConfig) error {
  473. SetXID(ctx, "123456")
  474. SetTxRole(ctx, Launcher)
  475. return nil
  476. },
  477. callback: callbackNil,
  478. mockTimeoutTarget: isTimeout,
  479. mockTimeoutFunc: func(ctx context.Context) bool {
  480. return true
  481. },
  482. mockRollbackTargetName: "Rollback",
  483. mockRollbackFunc: func(_ *GlobalTransactionManager, ctx context.Context, gtr *GlobalTransaction) error {
  484. return fmt.Errorf("tm detected a timeout but rollback threw an exception")
  485. },
  486. },
  487. }
  488. for i, v := range gts {
  489. t.Logf("Case %v: %+v", i, v)
  490. var beginStub *gomonkey.Patches
  491. var secondStub *gomonkey.Patches
  492. var timeoutStub *gomonkey.Patches
  493. var rollbackStub *gomonkey.Patches
  494. if v.mockBeginTarget != nil {
  495. beginStub = gomonkey.ApplyFunc(v.mockBeginTarget, v.mockBeginFunc)
  496. }
  497. if v.mockSecondPhaseTarget != nil {
  498. secondStub = gomonkey.ApplyFunc(v.mockSecondPhaseTarget, v.mockSecondPhaseFunc)
  499. }
  500. if v.mockTimeoutTarget != nil {
  501. timeoutStub = gomonkey.ApplyFunc(v.mockTimeoutTarget, v.mockTimeoutFunc)
  502. }
  503. if v.mockRollbackTargetName != "" {
  504. rollbackStub = gomonkey.ApplyMethod(reflect.TypeOf(GetGlobalTransactionManager()), v.mockRollbackTargetName, v.mockRollbackFunc)
  505. }
  506. ctx := context.Background()
  507. err := WithGlobalTx(ctx, v.GtxConfig, v.callback)
  508. if v.occurError {
  509. assert.Equal(t, v.errMessage, err.Error())
  510. }
  511. if v.callbackErr {
  512. assert.NotNil(t, err)
  513. }
  514. if v.secondErr {
  515. assert.NotNil(t, err)
  516. }
  517. if v.timeoutErr {
  518. assert.Regexp(t, v.errMessage, err.Error())
  519. }
  520. if v.mockBeginTarget != nil {
  521. beginStub.Reset()
  522. }
  523. if v.mockSecondPhaseTarget != nil {
  524. secondStub.Reset()
  525. }
  526. if v.mockTimeoutTarget != nil {
  527. timeoutStub.Reset()
  528. }
  529. if v.mockRollbackTargetName != "" {
  530. rollbackStub.Reset()
  531. }
  532. }
  533. }